Apontadores (Ponteiros)
Um apontador é uma variável que contém o endereço de um dado ou de um código de programa. Podemos obter o endereço de uma variável usando a função Addr ou o operador @.
var N :
integer;
begin
writeln('Addr(N)=', longint(Addr(N)));
writeln('@N=', longint(@N))
end.
Variável tipo Pointer
Usa-se uma variável tipo Pointer para armazenar um endereço de memória.
var <nome-da-variável> : Pointer;
Este tipo de apontador pode ser usado para guardar o endereço de qualquer variável, independente do tipo da mesma.
var
N : integer;
R : real;
S : String[50];
P : Pointer;
begin
P := Addr(N); {1}
writeln('Addr(N)=', longint(P));
P := Addr(R); {2}
writeln('Addr(R)=', longint(P));
P := @S; {3}
writeln('Addr(S)=', longint(P));
P := @P; {4}
writeln('Addr(P)=', longint(P));
end.
Supondo que N, R, S e P estão juntos na memória, teríamos uma organização semelhante à abaixo:
Porém, as variáveis dinâmicas no Free Pascal são normalizadas para ocupar pedaços de memória de 16, 32 ou 64 bytes (o menor possível). Sendo assim, teríamos o resultado abaixo:
Apontadores Tipados
São apontadores que podem ser usados para referenciar um tipo específico. São declarados da seguinte forma:
var <nome-da-variável> : ^<tipo>;
type
TItem = record
Nome : String;
Preco : real;
end;
var
PItem : ^TItem;
Item : TItem;
begin
Item.Nome := 'Livro de Ed1';
Item.Preco := 25.0;
PItem := Addr(Item);
end.
Desreferenciando um Apontador
Desreferenciar um apontador faz com que obtenhamos o valor contido no endereço apontado.
<identificador>^
A atribuição de apontadores é igual a uma variável normal.
var
X : integer;
P1, P2 : ^integer;
begin
X := 10;
P1 := @X;
writeln(X);
writeln(P1^);
P2 := P1;
P2^ := 100;
writeln(X)
end.
A Constante Nil
É uma constante que representa um endereço de memória inválido, ou seja, um apontador com esse valor não aponta para nada.
Variáveis Dinâmicas
São variáveis que são criadas e destruídas durante a execução do programa (dinamicamente e sob demanda). Não são associados a um identificador. Não são declaradas na seção de declaração de variáveis. São sempre referenciadas através de apontadores.
Permitem melhor gerenciamento da memória, já que podem ser alocadas sob demanda e liberadas assim que não forem mais necessárias.
Suportam a criação de estruturas de dados complexas de forma eficiente, como listas encadeadas e árvores.
Em certas situações, são mais eficientes do que estruturas estáticas, permitindo que o programador decida qual é melhor em cada situação.
Porém, o uso de variáveis dinâmicas está associada a mais erros de programação, já que a alocação e desalocação de memória fica a cargo do programador.
Alocação e Desalocação de Variáveis Dinâmicas
Há uma função chamada new que dado um apontador cria uma variável dinâmica do tipo do apontador e faz com que o apontador tipado passado aponte para a variável criada.
new (<apontador-tipado>)
Para liberar a memória alocada pela função new, usa-se o procedimento dispose.
dispose (<apontador-tipado>)
type
TItem = record
Nome : String[20];
Preco : real
end;
var
PItem, PAnterior : ^TItem;
begin
New(PItem);
PItem^.Nome := 'Livro de ED1';
PItem^.Preco := 30.0;
PAnterior := PItem;
New(PItem);
PItem^.Nome := 'Outro livro';
PItem^.Preco := 10.0;
writeln(PAnterior^.Nome);
writeln(PAnterior^.Preco);
writeln(PItem^.Nome);
writeln(PItem^.Preco);
Dispose(PAnterior);
Dispose(PItem);
PItem := nil;
PAnterior := nil
end.
type
PItem = ^TItem;
TItem = record
Nome : string[20];
Preco : real;
Proximo : PItem
end;
var
Head, PNew, PAtual : PItem;
Resp : char;
begin
Head := nil;
repeat
New(PNew);
writeln('Digite o nome e o preco do Item:');
readln(PNew^.Nome, PNew^.Preco);
PNew^.Proximo := Head;
Head := PNew;
write('Outro Item (S/N)?');
readln(Resp);
until UpCase(Resp) = 'N';
writeln('Produtos
Lidos:');
PAtual := Head;
while PAtual <> nil do
begin
writeln(PAtual^.Nome, ' - ',
PAtual^.Preco);
PAtual := PAtual^.Proximo;
end
end.
Alocando e Desalocando Memória usando GetMem e FreeMem
GetMem(<apontador>, <número-de-bytes>)
FreeMem(<apontador>, <número-de-bytes>)
A Lista de Blocos Livres
Ao desalocar uma variável dinâmica, normalmente cria-se um espaço vazio na memória. O endereço e o tamanho desta variável são incluídos na lista de blocos livres. As funções New e GetMem verificam se há um bloco livre na lista com o tamanho desejado.
O uso desta lista de blocos livres permite que áreas de memória fornecidas pelo sistema operacional sejam reaproveitadas para outras variáveis dinâmicas sem que seja necessário alocar mais memória.
As funções MaxAvail e MemAvail
MaxAvail retorna o tamanho do maior bloco de memória livre, incluindo o bloco que começa no topo do heap.
MemAvail retorna a soma dos tamanhos de todos os blocos livres.
var
P1, P2 : Pointer;
begin
writeln('MaxAvail:', MaxAvail, ', MemAvail:', MemAvail);
GetMem(P1, 5000);
GetMem(P2, 1000);
writeln('MaxAvail:', MaxAvail, ', MemAvail:', MemAvail);
FreeMem(P1, 5000);
writeln('MaxAvail:', MaxAvail, ', MemAvail:', MemAvail);
FreeMem(P2, 1000);
writeln('MaxAvail:', MaxAvail, ', MemAvail:', MemAvail);
end.
As funções MaxAvail e MemAvail só funcionam no Turbo Pascal. Isto ocorre porque o Free Pascal é um compilador para sistemas operacionais modernos e que permitem vários processos em paralelo e não limita a quantidade de memória disponível para um programa, como ocorre com o DOS. Sendo assim, mesmo que as funções retornariam uma quantidade de memória que muito provavelmente já não seria válida porque outros processos estão alocando e desalocando memória em paralelo.
Desta forma, com o Free Pascal devemos tentar alocar a quantidade de memória e testar se tivemos sucesso, ou simplesmente não testar e deixar o programa encerrar por falta de memória.
Cuidados com o Dispose
Após executar Dispose o apontador continua apontado para o mesmo endereço
Erros Comuns com Apontadores