Ponteiros — Básico
Ponteiros são como endereços de rua — o endereço em si não é a casa, mas através dele você encontra aquela casa exata. Domine endereços, e você dominará o recurso mais poderoso de C.
O que é um ponteiro?
Cada byte na memória tem um número único chamado endereço. Uma variável ponteiro é uma variável projetada especificamente para armazenar um endereço.
int x = 42;
int *p = &x;
xé uma variável inteira com valor 42&xé o endereço dexna memóriapé uma variável ponteiro que armazena o endereço dex*pacessa o valor dexatravés de seu endereço, que é 42
Ponteiros também ocupam memória. Em um sistema de 32 bits, um ponteiro tem 4 bytes; em um sistema de 64 bits, tem 8 bytes, independentemente do tipo para o qual aponta.
O operador de endereço &
& é usado para obter o endereço de uma variável:
int a = 10;
printf("Value of a: %d\n", a);
printf("Address of a: %p\n", (void *)&a);
O especificador de formato %p imprime um endereço; o argumento deve ser convertido para void *.
Do que você pode obter o endereço:
- Variáveis:
&x - Elementos de vetores:
&arr[3] - Membros de struct:
&s.age
Do que você não pode obter o endereço:
- Literais:
&10(ilegal) - Variáveis register:
&r(seréregister) - Resultados de expressões:
&(a+b)(ilegal)
O operador de desreferência *
* é usado para acessar os dados que um ponteiro aponta:
int x = 42;
int *p = &x;
printf("%d\n", *p);
*p = 100;
printf("%d\n", x);
A primeira saída é 42, a segunda é 100. *p = 100 modifica x através do ponteiro.
* em uma declaração significa "isto é um ponteiro"; em uma expressão significa "desreferenciar" (obter o valor apontado). O significado depende do contexto.
Desreferenciar um ponteiro não inicializado é um erro grave:
int *p;
*p = 10;
O valor de p é aleatório; escrever em um endereço aleatório pode corromper outros dados ou causar falha imediata.
O ponteiro NULL
NULL é um valor especial de ponteiro que significa "não aponta para lugar nenhum". Está definido em vários cabeçalhos incluindo <stddef.h>, e seu valor é tipicamente 0.
int *p = NULL;
if (p == NULL) {
printf("Pointer does not point to a valid address\n");
}
Desreferenciar um ponteiro NULL causa uma falha de segmentação e encerra o programa. Portanto, sempre verifique se um ponteiro é NULL antes de usá-lo:
void safe_print(int *p) {
if (p != NULL) {
printf("%d\n", *p);
}
}
Tipos de ponteiro e passo
O tipo do ponteiro determina quantos bytes são lidos durante a desreferência, e quantos bytes o ponteiro se move durante aritmética (o passo).
int a = 10;
double b = 3.14;
int *pi = &a;
double *pd = &b;
*pilê 4 bytes (tamanho de int),*pdlê 8 bytes (tamanho de double)pi+1move 4 bytes para o próximo int,pd+1move 8 bytes para o próximo double
O passo do ponteiro equivale ao tamanho do tipo apontado:
int arr[] = {10, 20, 30, 40, 50};
int *p = arr;
printf("%d\n", *p);
printf("%d\n", *(p + 1));
printf("%d\n", *(p + 3));
Saída: 10, 20, 40. p+1 não adiciona 1 ao valor do endereço; adiciona sizeof(int) (4 bytes), apontando para o próximo elemento int.
A subtração de ponteiros também é medida em elementos:
int *q = &arr[4];
printf("%ld\n", (long)(q - p));
Saída: 4, significando que 4 elementos int separam os dois ponteiros.
int *p = &d; (onde d é double) produzirá um aviso do compilador porque a incompatibilidade de tipos faz com que o número errado de bytes seja lido na desreferência.
Exemplo
Troque os valores de duas variáveis usando ponteiros:
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main(void) {
int x = 10, y = 20;
printf("Before swap: x=%d, y=%d\n", x, y);
swap(&x, &y);
printf("After swap: x=%d, y=%d\n", x, y);
return 0;
}
Before swap: x=10, y=20
After swap: x=20, y=10
Aqui, endereços são passados à função, que modifica as variáveis originais através de desreferência. Essa é a técnica clássica para "simular passagem por referência" — o parâmetro da função é um ponteiro int *, e a chamada passa &x.
Ponteiros e const
Combinar const com ponteiros tem dois significados:
Ponteiro para uma constante
const int *p = &x;
*p = 20;
Ilegal. Você não pode modificar os dados através de p, mas p pode apontar para uma variável diferente:
p = &y;
Legal.
Ponteiro constante
int * const p = &x;
p = &y;
Ilegal. p não pode mudar o que aponta, mas você pode modificar os dados através de p:
*p = 20;
Legal.
Truque de memorização: const à esquerda de * modifica os dados; const à direita de * modifica o ponteiro em si.
Exemplo
Some um vetor usando percurso com ponteiro:
#include <stdio.h>
int array_sum(const int *arr, int len) {
int sum = 0;
const int *end = arr + len;
while (arr < end) {
sum += *arr;
arr++;
}
return sum;
}
int main(void) {
int data[] = {3, 7, 1, 9, 5};
int total = array_sum(data, 5);
printf("Sum: %d\n", total);
return 0;
}
Sum: 25
O parâmetro const int *arr indica que a função não modificará o conteúdo do vetor. O ponteiro end marca a posição de parada, evitando a necessidade de índices.
❓ Perguntas Frequentes
P: Quantos bytes uma variável ponteiro ocupa? R: Depende da largura de endereço do sistema: 4 bytes em sistemas de 32 bits, 8 bytes em sistemas de 64 bits, independentemente do tipo apontado.
P: O
*emint *ppertence ao tipo ou à variável? R: Pertence à variável.int* p, q;declara p como ponteiro para int e q como int simples. É recomendável declarar apenas um ponteiro por linha.
P: Qual a diferença entre NULL e um ponteiro não inicializado? R: NULL é um valor definido (0) que pode ser verificado; um ponteiro não inicializado tem um valor aleatório e não pode ser detectado de forma confiável, tornando-o mais perigoso.
P: Desreferenciar um ponteiro NULL sempre causa falha? R: Não garantido. O endereço 0 pode ser um endereço válido em alguns sistemas embarcados. No entanto, em sistemas operacionais mainstream, acessar o endereço 0 provoca uma falha de segmentação.
📖 Resumo
- Ponteiros armazenam endereços;
&obtém o endereço,*desreferencia para obter o valor - Ponteiros não inicializados são ponteiros selvagens; sempre inicialize com NULL
- O tipo do ponteiro determina o passo e o número de bytes lidos na desreferência
- Passar um ponteiro a uma função permite que ela modifique as variáveis de quem chamou
constcom ponteiros tem duas posições: modificando os dados ou modificando o ponteiro em si
📝 Exercícios
- Escreva uma função
void divide(int a, int b, int *quotient, int *remainder)que retorne o quociente e o resto através de parâmetros de ponteiro. - Escreva um programa que defina uma variável int e uma double, aponte para elas com
int *edouble *respectivamente, imprima seus endereços e valores, e observe a diferença de passo. - Escreva uma função
int *find_max(int *arr, int len)que retorne um ponteiro para o elemento máximo no vetor.



