Uma confusão recorrente… pointeiros e arrays.

Alguns de vocês já devem ter lido por aqui e feito experiências com relação ao “endereço” base de um array. Considere o seguinte array:

int a[] = { 1, 2, 3, 4 };

O identificador a é, de forma bastante comum, encarado como o ponteiro para o início do array. De fato, a especificação ISO 9989 nos diz que o operador [] pode ser, seguramente, interpretado como a[i] = *(a + i). Mas, há uma distinção sutil aqui: O identificador a não é um ponteiro! Ele é interpretado como sendo um…

“Porra, tio Fred, assim você dá um nó na minha cachola!”. O problema aparece quando usamos o operador & (endereço-de):

#include <stdio.h>

int a[] = { 1, 2, 3 4 };

int main( void )
{
  printf( "a=%p, &a=%p\n", a, &a );
}

Ao compilar e executar isso você verá que os endereços impressos são os mesmos. Como pode um ponteiro apontar para uma coisa e um ponteiro para um ponteiro apontar para a mesma coisa? Não pode! O fato é que a, usando usado em expressões, é convertido em um ponteiro, mas ele não é um. O símbolo a aqui é um array “identificado” pelo nome a.

Parece que não há diferença, não é? Então, vejamos esse outro exemplo: Se a e &a forem, de fato, o mesmo ponteiro, apontar para o próximo item do array deveria fornecer o mesmo endereço, não é?

#include <stdio.h>

int a[] = { 1, 2, 3 4 };

int main( void )
{
  printf( "a=%p, &a=%p\n", a, &a );
  printf( "next(a)=%p, next(&a)=%p\n", a + 1, &a + 1 );
}

Compilando e executando você obtém algo assim:

$ cc -O2 -o test test.c
$ ./test
a=0x563c50e5a010, &a=0x563c50e5a010
next(a)=0x563c50e5a014, next(&a)=0x563c50e5a020

Ué?! Por que &a+1 resultou em um endereço diferente de a+1? Note que em nossa tentativa de obter o endereço do próximo item com o ponteiro, no primeiro caso obtemos, corretamente, o endereço 4 bytes além da base, mas no segundo (usando &a), obtemos o endereço além do final do array (16 bytes além da base)… Isso ocorre porque os tipos dos ponteiros convertidos à partir de a são diferentes… O primeiro ponteiro, a+1 é do tipo int *, o segundo, do tipo int (*)[4]. Ou seja, a e &a fornecem o mesmo endereço porque estamos obtendo o endereço do identificador a, que aponta para o início do array. No primeiro caso (a) o identificador é convertido para um ponteiro, no segundo (&a) obtemos o “endereço-do array identificado por a.

Sim, você pode assumir que o identificador de um array seja um ponteiro para sua base, se usar apenas o nome do identificador como ponteiro (não o seu endereço), mas, além do problema acima, existe outro: O identificador de um array definido carrega consigo, em tempo de compilação, o tamanho do array. Se você usar o operador sizeof com o identificador a, obterá 16 (4 vezes o tamanho de um int), no exemplo acima… Se a fosse apenas um ponteiro, obteria 4 (i386) ou 8 (x86-64) que, aliás, é o que obterá se tentar obter sizeof(&a).