Fundamentos sobre ponteiros

Para compreender os ponteiros, convém compará-los às variáveis normais.

Uma "variável normal" é um local na memória que pode conter um valor. Por exemplo, quando você declara uma variável i como um inteiro, 4 bytes de memória são separados para isso. Em seu programa, você se refere àquele local na memória pelo nome i. No nível da máquina, aquele local tem um endereço de memória. Os 4 bytes naquele endereço são conhecidos pelo programador como i, e os 4 bytes podem conter valores inteiros.

Um ponteiro é diferente. Um ponteiro é uma variável que aponta para outra variável. Isto significa que um ponteiro mantém o endereço de memória de outra variável. Em outras palavras, o ponteiro não contém um valor no sentido tradicional, mas sim o endereço de outra variável. Um ponteiro "aponta para" esta outra variável mantendo uma cópia de seu endereço.

Como um ponteiro contém um endereço, e não um valor, terá duas partes. O ponteiro contém um endereço e o endereço aponta para um valor. Há o ponteiro e o valor para o qual ele aponta. Este fato pode ser um tanto confuso, até você se familiarizar com ele. Superada a etapa da familiarização, ele se torna extremamente eficaz.

A codificação exemplificada a seguir mostra um típico ponteiro:

  #include <stdio.h>  

  int main()
  {
      int i,j;
      int *p;
   /* um ponteiro para um inteiro */
      p = &i; 
      *p=5; 
      j=i;
      printf(("%d %d %d\n", i, j, *p);
      return 0;
  }  

A primeira declaração neste programa mostra duas variáveis inteiras normais, nomeadas i e j . A linha int *p declara um ponteiro denominado p. Esta linha pede que o compilador declare uma variável p que seja um ponteiro para um inteiro. O * indica que foi declarado um ponteiro em vez de uma variável normal. Você pode criar um ponteiro para qualquer coisa: um flutuante, uma estrutura, um caractere e assim por diante. Basta usar um * para indicar que deseja um ponteiro em vez de uma variável normal.

A linha p = &i; trará algo totalmente novo para você. Em linguagem C, & é denominado operador de endereço. A expressão &i significa: "O endereço de memória da variável i". Assim, a expressão p = &i; significa: "Atribuir para p o endereço de i". Uma vez executada esta instrução, p "aponta para" i. Antes de prosseguir, lembre-se que p contém um endereço aleatório e desconhecido e que sua utilização causará uma falha de segmentação ou erro de programa similar.

Um bom método para visualizar o que está acontecendo é fazer um desenho. Depois que i, j e p são declarados, as coisas parecerão assim:

Neste desenho, as três variáveis i, j e p foram declaradas, mas nenhuma delas foi inicializada. As duas variáveis inteiras foram desenhadas como caixas contendo os pontos de interrogação (elas poderiam conter qualquer valor nesta altura da execução do programa). O ponteiro é desenhado como um círculo para distingüi-lo de uma variável normal que contém um valor e as setas aleatórias indicam que ele pode estar apontando para qualquer lugar neste momento.

Depois da linha p = &I;, p é inicializado e aponta para i, desta forma:

Assim que p aponta para i, o local de memória i tem 2 nomes. Ele ainda é conhecido por i , mas agora também é conhecido como *p. É assim que a linguagem C se comunica com as duas partes de uma variável de ponteiro: p é o local que mantém o endereço, enquanto *p é o local apontado por aquele endereço. Logo, *p=5 significa que o local apontado por p deve ser definido como 5, assim:

Visto que o local *p também é i, i também assume o valor 5. Conseqüentemente, j=i; define j como 5 e a instrução printf produz 5 5 5.

A principal característica de um ponteiro é sua dupla natureza. O próprio ponteiro contém um endereço. O ponteiro também aponta para um valor de um tipo específico: o valor no endereço do ponteiro. O próprio ponteiro, neste caso, é p. O valor apontado é *p.