Aula 14: Ponteiros

Traduzido por Ana Paula Costa

Ponteiro

Um ponteiro é um tipo especial de variável. Por si só não contém informação útil. É apenas um endereço para uma localização que (talvez) contém informação útil.
 Um ponteiro contém o endereço de uma localização em memória 

Isto é como manter o endereço de um apartamento na agenda. É apenas um endereço e nada mais.


Declaração

Para declarar um ponteiro podemos utilizar a seguinte sintaxe:
 
 tipo *nome;
Sendo nome o nome da variável ponteiro e tipo o tipo de informação a que o ponteiro está a apontar. Isto pode ser qualquer tipo dos que já conhecemos, desde simples reais e inteiros, a tipos mais complicados que iremos aprender mais tarde.
Exemplos:

  int *p;
Agora p é um ponteiro que aponta para informação do tipo int.

  float *f;
Agora f é um ponteiro que aponta para informação do tipo float. O valor de f em si não é do tipo float, porque f é um ponteiro e o seu valor é um endereço. Apenas o conteúdo do endereço de memória para o qual f está a apontar contém informação do tipo float.


Operações com ponteiros

Existem duas operações com ponteiros em C.
 
 
 &x retorna o endereço da (aponta para a) variável
 *p é o valor que está a ser apontado por p (conteúdo do endereço p) 

 
Vamos declarar uma variável do tipo float, x, e um ponteiro para floats, p:
  float x;
  float *p;

Na figura da esquerda, x é uma variável do tipo float que tem o valor de 3.0. Como queremos que o ponteiro p fique a apontar para a variável x, podemos escrever
  p = &x;

O valor de p é agora um endereço de memória, nomeadamente o endereço que contém a variável x. Para mostrar o conteúdo da localização de memória a que o ponteiro p está a apontar, podemos fazer
  printf("%f", x);
ou através do ponteiro 
  printf("%f", *p);


Especificação do tipo

Porque razão é tão importante que se especifique o tipo para o qual o ponteiro aponta? Isto consegue-se mostrar melhor com um exemplo.

Vamos imaginar a seguinte situação: A figura em baixo mostra parte da memória e o seu conteúdo. Um ponteiro p aponta para uma localização nesta memória. Se p está a apontar para um byte(unsigned char) como representado na linha de cima, o conteúdo do endereço p será *p =129 (binary: 10000001), enquanto com p a apontar para o mesmo local da memória, mas a apontar para um unsigned int *p = 25473 (binary: 0110001110000001), ou p a apontar para um long int (4 bytes, 32 bits) irá dar 743924609 (binary: 00101100010101110110001110000001). (De notar que nos computadores baseados no processador Intel, os números são armazenados com o seu bit de valor menos significativo [LSB=least significant bit] primeiro).

Assim sendo, temos que especificar o tipo a que o ponteiro irá apontar.


Exemplo

Vamos agora ver um exemplo para verificar se está tudo sobre controlo. Vamos criar um ponteiro do tipo apontador para uma word (unsigned int), e deixá-lo apontar para uma word.
 

void main()
{
    /* declare a word (unsigned int) and  a pointer to word: */
  unsigned int *wordptr;
  unsigned int w;

    /* assign a value to the word */
  w = 25473;

    /* let a 'pointer to word' point to our word */
  wordptr = &w;
    /* show the contents of the memory wordptr points to */
  printf("%d", *wordptr);
}

output:
  25473

Vamos ver agora um exemplo mais complicado. Vamos criar um ponteiro do tipo apontador para um byte (unsigned char), e apontá-lo para a nossa word unsigned int:

void main()
{
    /* declare a word (unsigned int) and  a pointer to word: */
  unsigned char *charptr;
  unsigned int w;

    /* assign a value to the word */
  w = 25473;

    /* let a 'pointer to char' point to our word */
  charptr = &w;
    /* show the contents of the memory wordptr points to */
  printf("%d", *charptr);
}

output:
 129

(output for Borland C++ version 3.1 for MS-DOS. On other computers or versions the output might be different)
Isto mostra como temos que ser cuidadosos com o que o nosso ponteiro aponta. O valor depende do tipo! 


Pointers and arrays in C

In C, all arrays are pointers. What this means is that when we declare an array
  int a[10];
this will
  - reserve 10x2 bytes in memory
  - assign the address of this memory to a
Therefore,
  a is of type 'pointer to int' and the value of a is an address.
 *a is the contents of address a. In this address resides the first element of a, namely a[0].
Therefore, the two forms, *a and a[0] are completely interchangeable and any of the two can be used at any time. We will see this later, when we discuss strings (arrays of char).
We have to remember this when we pass information to a function; when we pass an array, we pass the address of the array, rather than all the elements of the array. With arrays in C we always use the technique of passing by reference (see lecture 15).

Initialization

Initialization of a variable is even more important for pointers. Without initialization, a pointer points to a random part of memory, where important programs might be running. These programs and the computer can crash when we write in this place. The following program might crash the computer. It defines a pointer and doesn't assign an address to it. The value of the pointer (the address) is therefore unpredictable. The program then writes a value in this random address.
void main()
{
  int *p;

  *p = 0;
}


Porquê?


Porquê utilizar ponteiros? Existem várias razões para se usarem ponteiros em vez de variáveis normais. As mais importantes são
 

  • Velocidade
  • Flexibilidade

Velocidade: Imaginemos que queremos escrever um programa que move uma série de informação. Na forma convencional, isto iria significar copiar muitos bytes de uma parte da memória para outra parte. Pensemos por exemplo na ordenação de um array.
(Um ponteiro são apenas 4 bytes nos computadores Intel).

Flexibilidade: Se, no início do programa nós ainda não sabemos quantas variáveis iremos precisar teríamos que reservar espaço para todas as eventualidades possíveis. Se queremos escrever um programa que calcule os primeiros N números primos, com N dado pelo utilizador, teríamos que criar um array de tamanho máximo para garantir que podemos dar resposta a qualquer pedido do utilizador. Isto iria ocupar completamente a memória do computador. Mais nada poderia correr a partir daí. Muito melhor seria se pudéssemos declarar o array (as variáveis) dinamicamente de forma a que fosse utilizada apenas a memória realmente necessária. Com ponteiros isto consegue-se facilmente.

Os ponteiros e a ideia da criação dinâmica de variáveis também são a base da programação orientada a objectos ("object-oriented programming"), que é o tipo de programação mais moderna e mais utilizada hoje em dia. A programação orientada a objectos está fora do âmbito desta aula.


Teste Rápido:
Para testar os conhecimentos sobre o que aprendeu nesta aula, prima aqui para fazer um teste on-line. De notar que este Não é o formato que será utilizado no teste final!

Peter Stallinga. Universidade do Algarve, 13 November 2002