quarta-feira, 24 de outubro de 2007

Entendendo Ponteiros na linguagem C

Entender ponteiros (e ponteiros para ponteiros) nem sempre é facil quando estamos vendo C ou C++ pela primeira vez. O conceito é absurdamente simples, assim como os operadores * e &, mas pelo menos para mim demorou um tempão para a ficha cair (e olha que eu utilizava arrays direto e nem me ligava nesse detalhe).

Com auxilio do pre-processador C, vejamos se este exemplo que pode ser elucidativo:

#include "pointer.h" 
int main ()
{
int x = 0; // variavel de exemplo
pointer(int) y; // ponteiro

y = address(x); // y aponta para o endereco de x

x++; // incrementamos o valor de x
value(y)++; // incremento indireto (via ponteiro)

printf("value %d %d\n",x, value(y) );
printf("address %p %p\n",address(x), y );

return 0;
}


Ou seja, a criação de um ponteiro para uma variavel do tipo inteira é através da macro pointer. Outra macro, address, retorna o endereço de uma variavel e, assim, tenho uma referência aquela variavel. Posso então ter acesso ao valor e até mesmo manipular a variavel original.

Vejamos agora o resultado da execução:
$ ./a.out
value 2 2
address 0xbfffeaa4 0xbfffeaa4


Perceba que eu consegui incrementar a variavel duas vezes (através das duas formas disponíveis). Perceba também que o ponteiro y tem como valor o mesmo endereço de memória que a variavel x, razão pela qual consigo alterar o seu valor.

O codigo gerado pelas macros

int main () 
{
int x = 0; // variavel de exemplo
int * y; // ponteiro

y = (&(x)); // y aponta para o endereco de x

x++; // incrementamos o valor de x
(*y)++; // incremento indireto (via ponteiro)

printf("value %d %d\n",x, (*y));
printf("address %p %p\n",(&(x)), y);

return 0;
}


Eu recomendo que, uma vez que o conceito tenha ficado claro, que não se use mais este header sob pena de criar arquivos ilegíveis. Por exemplo, tente compilar com a opção de gerar os simbolos para debug (-g) e depois tente debugar esse programa: perceberão que o gdb, por exemplo, não mostra o codigo fonte com tanta clareza.

Para fazer este exemplo, utilizamos este arquivo header:

#define pointer(type)                    type *
#define value(var) (*(var))
#define address(var) (&(var))