Aula 14: Ponteiros |
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.
tipo *nome; |
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.
&x retorna o endereço da (aponta para a) variável x |
|
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
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
|
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.
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!
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ê utilizar ponteiros? Existem
várias
razões para se usarem ponteiros em vez de variáveis
normais.
As mais importantes são
|
|
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.