next up previous contents
Next: Ponteiro como argumento de Up: Ponteiros Previous: Aritmética de ponteiros   Sumário

Ponteiros e arranjos

Como observado no exemplo acima, ponteiros e arranjos estão intimamente relacionados em C. Na verdade, qualquer referência a um arranjo é convertida internamente para uma referência do tipo ponteiro. Por este motivo, quando eficiência de tempo de acesso é uma preocupação, muitos programadores trabalham diretamente com ponteiros.

O nome de um arranjo é uma expressão do tipo ponteiro que corresponde ao endereço do primeiro elemento do arranjo. Assim, a inicialização do ponteiro no exemplo acima poderia ser reescrita como

    el = arr;   /* arr equivale a &arr[0] */

Observe que, uma vez que arr equivale a um ponteiro, o elemento arr[i] poderia ser acessado da mesma forma como *(arr+i). Na verdade, é isto que o compilador irá fazer internamente: qualquer expressão da forma E1[E2] será internamente traduzida para *((E1)+(E2)). Observe que isto implica que esta operação de indexação é comutativa, embora tal fato raramente seja utilizado em programação C.

Por outro lado, o inverso (usar o operador de indexação com uma variável ponteiro) também é possível. Assim, o laço de atribuição no exemplo acima poderia ter sido escrito como

      for (i=0; i<10; ++i)
          el[i] = 0;

Apesar da forma usando o operador * ser mais eficiente, programadores iniciantes muitas vezes acham mais simples entender o acesso usando o operador de indexação, e acabam preferindo esta forma.

Uma diferença fundamental entre um ponteiro e o nome de um arranjo é que o ponteiro é uma variável, enquanto que o nome de um arranjo é uma constante. Assim, expressões como arr++ ou &arr não fazem sentido.

Outra diferença que deve ser ressaltada é o fato de que a declaração de um arranjo efetivamente reserva o espaço para as variáveis, enquanto que a declaração de um ponteiro reserva apenas espaço para guardar um endereço. Considere o seguinte exemplo:

    /*
     *  Exemplo do uso indevido de ponteiro
     */
    main() {
      int *el;                /* el:  ponteiro para inteiro */
      int i;
      
      /* inicializa conteudo */
      for (i=0; i<10; ++i)
           el[i] = 0;         /* onde esta el[i]? */
    }
Uma vez que o ponteiro el não foi inicializado, a expressão el[i] pode estar apontando para qualquer posição da área de memória -- possivelmente, para alguma posição inválida. Observe que o fato de ter declarado o ponteiro não significa que esta variável possa ser utilizada como um arranjo. Para tal, o ponteiro deve estar com o endereço de alguma posição válida, seja através de uma atribuição envolvendo um arranjo, seja através do uso de rotinas de alocação dinâmica.

Uma vez que ponteiros são variáveis, nada impede que arranjos de ponteiros sejam definidos. De fato, uma declaração tal como

    int *aa[10];
define uma variável aa que é um arranjo de dez ponteiros para variáveis inteiras. Cada elemento deste arranjo, desde aa[0] até aa[9], é um ponteiro para inteiro(s) que tem a mesma propriedade que os ponteiros vistos até o momento.

Observe que esta forma suporta uma opção para trabalhar com arranjos multidimensionais, desde que respeitadas as diferenças entre ponteiros e arranjos. Arranjos de ponteiros trazem uma flexibilidade adicional pelo fato de que cada ``linha'' pode ter tamanho variável. No exemplo acima, cada ponteiro aa[i] pode estar apontando para um inteiro, para o primeiro elemento de um arranjo com diversos inteiros, ou mesmo para nenhum inteiro.


next up previous contents
Next: Ponteiro como argumento de Up: Ponteiros Previous: Aritmética de ponteiros   Sumário
Ivan L. M. Ricarte 2003-02-14