Ponteiros e Vetores
Vetores e ponteiros são como duas vistas da mesma pessoa — de ângulos diferentes apresentam formas diferentes, mas por baixo apontam para o mesmo bloco contíguo de memória.
O nome do vetor é o endereço do primeiro elemento
Na maioria das expressões, o nome do vetor converte-se automaticamente em um ponteiro para o primeiro elemento:
int arr[] = {10, 20, 30, 40, 50};
int *p = arr;
printf("%p\n", (void *)arr);
printf("%p\n", (void *)&arr[0]);
printf("%p\n", (void *)p);
Todos os três endereços são idênticos. arr e &arr[0] são equivalentes — ambos são o endereço do primeiro elemento.
Há exceções: sizeof(arr) retorna o tamanho do vetor inteiro, não o tamanho do ponteiro. &arr é um ponteiro para o vetor inteiro com tipo int (*)[5], e seu passo é o vetor inteiro.
printf("%zu\n", sizeof(arr));
printf("%zu\n", sizeof(p));
Em um sistema de 64 bits, esses imprimem 20 (5 x 4 bytes) e 8 (tamanho do ponteiro), respectivamente.
Percurso de vetores com ponteiros
Como um ponteiro pode apontar para elementos do vetor, você pode percorrer o vetor inteiro usando aritmética de ponteiros:
int arr[] = {10, 20, 30, 40, 50};
int *p;
int len = sizeof(arr) / sizeof(arr[0]);
for (p = arr; p < arr + len; p++) {
printf("%d ", *p);
}
printf("\n");
arr + len aponta para a posição logo após o último elemento, servindo como sentinela de término do laço. Esse é um padrão C muito comum.
Você também pode usar notação de índice:
int i;
for (i = 0; i < len; i++) {
printf("%d ", *(arr + i));
}
Ou:
int *p = arr;
int i;
for (i = 0; i < len; i++) {
printf("%d ", p[i]);
}
p[i] e *(p + i) são completamente equivalentes. O operador de índice é açúcar sintático para aritmética de ponteiros.
Aritmética de ponteiros
Ponteiros suportam um conjunto limitado de operações aritméticas:
Adição e subtração de inteiros
int arr[] = {10, 20, 30, 40, 50};
int *p = arr + 2;
printf("%d\n", *p);
printf("%d\n", *(p + 1));
printf("%d\n", *(p - 1));
Saída: 30, 40, 20. p+1 avança um int, p-1 recua um int.
Subtração de ponteiros
Subtrair dois ponteiros dentro do mesmo vetor retorna o número de elementos entre eles:
int *q = &arr[4];
printf("%ld\n", (long)(q - p));
Saída: 2, porque p aponta para arr[2] e q aponta para arr[4], uma diferença de 2 elementos.
Comparação de ponteiros
Ponteiros dentro do mesmo vetor podem ser comparados usando <, <=, >, >= para determinar posições relativas:
int *start = arr;
int *end = arr + len;
while (start < end) {
printf("%d ", *start);
start++;
}
Resumo do passo de ponteiros
| Tipo | Passo (bytes movidos por p+1) |
|---|---|
char * |
1 |
int * |
4 |
double * |
8 |
int (*)[5] |
20 |
Passo = sizeof(tipo apontado)
Equivalência entre ponteiros e índices de vetores
As duas formas de acesso a seguir são completamente equivalentes:
| Expressão | Forma equivalente |
|---|---|
arr[i] |
*(arr + i) |
&arr[i] |
arr + i |
p[i] |
*(p + i) |
&p[i] |
p + i |
O compilador converte operações de índice em aritmética de ponteiros. arr[3] e 3[arr] são até ambos legais em C (porque *(arr+3) == *(3+arr)), embora ninguém escreva o último.
No entanto, ponteiros e vetores têm diferenças fundamentais:
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;
arré um vetor;sizeof(arr)= 20; não pode ser reatribuídopé um ponteiro;sizeof(p)= 8 (64-bit); pode ser reatribuído para apontar para outro lugar
Exemplo
Inverta um vetor usando ponteiros:
#include <stdio.h>
void reverse(int *arr, int len) {
int *left = arr;
int *right = arr + len - 1;
while (left < right) {
int temp = *left;
*left = *right;
*right = temp;
left++;
right--;
}
}
int main(void) {
int data[] = {1, 2, 3, 4, 5, 6, 7};
int len = sizeof(data) / sizeof(data[0]);
int i;
reverse(data, len);
for (i = 0; i < len; i++) {
printf("%d ", data[i]);
}
printf("\n");
return 0;
}
7 6 5 4 3 2 1
Técnica de dois ponteiros: left move do início em direção ao fim, right move do fim em direção ao início, trocando os elementos que apontam até se encontrarem.
A natureza de vetores como parâmetros de função
Quando um vetor é passado a uma função, o compilador passa apenas o endereço do primeiro elemento; a informação do comprimento do vetor é perdida. Isso é o "decaimento do vetor para ponteiro":
void func(int arr[]) {
}
O parâmetro int arr[] é equivalente a int *arr. Dentro da função:
sizeof(arr)retorna o tamanho do ponteiro, não o tamanho do vetor- Você não pode usar
sizeofpara calcular o número de elementos - O comprimento deve ser passado como um parâmetro separado
void print_array(int *arr, int len) {
int i;
for (i = 0; i < len; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
Quatro assinaturas equivalentes:
void func(int *arr, int len);
void func(int arr[], int len);
void func(int arr[5], int len);
void func(int arr[100], int len);
O número entre colchetes é ignorado pelo compilador; meramente indica "isto é um ponteiro".
Exemplo
Remova duplicatas de um vetor ordenado usando ponteiros:
#include <stdio.h>
int unique(int *arr, int len) {
if (len == 0) return 0;
int *dst = arr;
int *src = arr + 1;
int *end = arr + len;
while (src < end) {
if (*src != *dst) {
dst++;
*dst = *src;
}
src++;
}
return (int)(dst - arr + 1);
}
int main(void) {
int data[] = {1, 1, 2, 2, 2, 3, 4, 4, 5};
int len = sizeof(data) / sizeof(data[0]);
int new_len = unique(data, len);
int i;
for (i = 0; i < new_len; i++) {
printf("%d ", data[i]);
}
printf("\n");
return 0;
}
1 2 3 4 5
dst rastreia a posição de escrita dos dados sem duplicatas; src percorre o vetor original. A função retorna o novo comprimento. Este é um algoritmo de deduplicação in-place que não requer um vetor extra.
❓ Perguntas Frequentes
P: O nome de um vetor pode ser atribuído? R: Não.
arr = p;é ilegal; o nome do vetor é um ponteiro constante. Masp = arr;é legal porque uma variável ponteiro pode ser atribuída.
P: Qual a diferença entre
arre&arr? R:arré o endereço do primeiro elemento, tipoint *, passo sizeof(int);&arré o endereço do vetor inteiro, tipoint (*)[5], passo sizeof(int[5]). O valor numérico é o mesmo, mas os tipos diferem.
P: Por que não posso usar sizeof em um vetor dentro de uma função? R: Quando um vetor é passado como parâmetro, ele decai para um ponteiro, e sizeof retorna o tamanho do ponteiro (4 ou 8), não o tamanho do vetor. O comprimento deve ser passado separadamente.
P:
p[-1]é legal? R: Sintaticamente legal, equivalente a*(p-1). Se p aponta para o meio de um vetor,p[-1]acessa o elemento anterior com segurança. Mas acessar fora dos limites do vetor é comportamento indefinido.
📖 Resumo
- O nome de um vetor decai para um ponteiro para o primeiro elemento em expressões, exceto com sizeof e &
arr[i]e*(arr+i)são completamente equivalentes; índices são açúcar sintático para aritmética de ponteiros- Aritmética de ponteiros opera em unidades de passo, onde passo = sizeof(tipo apontado)
- Vetores decaem para ponteiros quando passados como parâmetros de função; o comprimento deve ser passado separadamente
- A técnica de dois ponteiros resolve eficientemente problemas como inversão e deduplicação
📝 Exercícios
- Escreva uma função que rotacione um vetor uma posição à esquerda (movendo o primeiro elemento para o final) usando ponteiros, sem um vetor extra.
- Escreva uma função
int *find(int *arr, int len, int target)que retorne um ponteiro para o primeiro elemento igual ao alvo, ou NULL se não encontrado. - Dado um vetor ordenado, implemente busca binária usando ponteiros. Retorne um ponteiro para o elemento encontrado, ou NULL se não encontrado.



