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:

C
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.

C
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:

C
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:

C
int i;
for (i = 0; i < len; i++) {
    printf("%d ", *(arr + i));
}

Ou:

C
int *p = arr;
int i;
for (i = 0; i < len; i++) {
    printf("%d ", p[i]);
}
💡 Dica: 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

C
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:

C
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:

C
int *start = arr;
int *end = arr + len;
while (start < end) {
    printf("%d ", *start);
    start++;
}
⚠️ Atenção: Aritmética de ponteiros só tem significado dentro do mesmo vetor. Subtrair ou comparar ponteiros de vetores diferentes é comportamento indefinido.

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:

C
int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;

Exemplo

Inverta um vetor usando ponteiros:

C
#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;
}
▶ Experimente
TEXT
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":

C
void func(int arr[]) {
}

O parâmetro int arr[] é equivalente a int *arr. Dentro da função:

C
void print_array(int *arr, int len) {
    int i;
    for (i = 0; i < len; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

Quatro assinaturas equivalentes:

C
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:

C
#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;
}
▶ Experimente
TEXT
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. Mas p = arr; é legal porque uma variável ponteiro pode ser atribuída.

P: Qual a diferença entre arr e &arr? R: arr é o endereço do primeiro elemento, tipo int *, passo sizeof(int); &arr é o endereço do vetor inteiro, tipo int (*)[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

📝 Exercícios

  1. 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.
  2. 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.
  3. Dado um vetor ordenado, implemente busca binária usando ponteiros. Retorne um ponteiro para o elemento encontrado, ou NULL se não encontrado.
100%