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 @.

Exemplo 1: Obtendo o endereço de uma variável integer:

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.

Exemplo 2: Atribuindo endereços de variáveis a um apontador:

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));
end.

 

Supondo que N, R, S e P estão juntos na memória, teríamos uma organização semelhante à 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>;

Exemplo 3: Crie um apontador para um Item que possui um nome e um preço:

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.

Exemplo 4: 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>)

Exemplo 5: Usando new e dispose para trabalhar com variáveis dinâmicas.

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.

 

 

Exemplo 6: Usando variáveis dinâmicas para ler e guardar na memória uma lista de itens.

type
   PItem = ^TItem;
   TItem = record
      Nome : string[20];
      Preco : real;
      Proximo : PItem
   end;
var
   Head : PItem;
   PNew : 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';
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.

 

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.

Exemplo 7: Usando GetMem, FreeMem, MaxAvail e MemAvail.

var
   P : Pointer;
begin
   writeln('MaxAvail:', MaxAvail, ', MemAvail:', MemAvail);
   GetMem(P, 5000);
   writeln('MaxAvail:', MaxAvail, ', MemAvail:', MemAvail);
   FreeMem(P, 5000);
   writeln('MaxAvail:', MaxAvail, ', MemAvail:', MemAvail);
end.

 

Cuidados com o Dispose


Após executar Dispose o apontador continua apontado para o mesmo endereço

 

Erros Comuns com Apontadores