Nos últimos artigos sobre ponteiros, foi explicado o que é e o como fazer para o ponteiro apontar para um variável existente, neste irei explicar como usar o ponteiro para apontar para um endereço de memória alocado dinamicamente.
Vimos que para um ponteiro apontar para uma variável, bastava lermos o endereço de memória da variável usando o operador @ e atribuír-lo ao ponteiro:
p := @i;
E caso quisessemos escrever onde o ponteiro estava apontando usamos o operador de dereferência ^:
p^ := 1;
Além dos ponteiros apontarem para endereços de memória já existentes, eles também podem ser usados para apontar para endereços de memória que são alocados. No Pascal / Delphi para alocar um bloco de memória temos o procedimento GetMem:
procedure GetMem(var P: Pointer; Size: Integer);
No primeiro parâmetro passamos uma variável do tipo ponteiro e no segundo o tamanho em byte que queremos alocar. Para sabermos o tamanho da memória que queremos alocar podemos usar a função SizeOf:
function SizeOf(X): Integer;
X pode ser uma variável ou um tipo, esta função retorna o número de bytes que aquela variável ou tipo precisam.
Quando não precisarmos mais deste bloco de memória alocado, podemos utilizar o procedimento FreeMem:
procedure FreeMem(var P: Pointer[; Size: Integer]);
Ele faz o oposto do GetMem, desalocando o bloco de memória apontado pelo ponteiro passado no primeiro parâmetro.
Exemplo de alocação dinâmica:
{$APPTYPE CONSOLE}
uses SysUtils;
type
PInteger = ^Integer;
var
p: PInteger;
begin
GetMem(p, SizeOf(Integer));
try
p^ := 2;
WriteLn(Format(‘%d’, [p^]));
finally
FreeMem(p, SizeOf(Integer));
end;
ReadLn;
end.
Inicialmente declaramos p como um ponteiro que apontará para um Integer, depois alocamos um espaço em memória de tamanho SizeOf(Integer) bytes (nos compiladores atuais de 32 bits, um Integer ocupa 32 bits ou 4 bytes). A partir deste momento em p é retornado este endereço alocado.
A instrução p^ := 2; faz atribuir o valor 2 como se fosse um Integer na região de memória alocada, na instrução seguinte é mostrado o conteúdo da memória como se fosse um Integer na tela.
Finalizando temos a desalocação da memória. Isso diz para o sistema operacional que a memória pode ser alocada para outra função. Veja que colocamos o código dentro de um bloco try..finally..end; para garantir que se alguma excessão ocorrer durante a utilização do bloco de memória alocado, esta memória seja desalocada. Assim evitando o aparecimento de uma vazamento de memória (memory leak). O segundo parâmetro do procedimento FreeMem é opcional, coloquei no nosso exemplo apenas para entendermos o processo, internamente quando se usa o GetMem, é guardado o tamanho do espaço alocado, assim quando se chama o FreeMem para um determinado endereço, o FreeMem já saberá o quanto de memória é necessário desalocar.
O GetMem e o FreeMem são os equivalentes ao malloc e free da linguagem C. Em Pascal / Delphi, contamos ainda com um par de procedimentos:
procedure New(var P: Pointer);
procedure Dispose(var P: Pointer);
O New e o Dispose alocam o tamanho de memória que o ponteiro P aponta. Assim não precisamos especificar o tamanho em bytes a ser alocado. O nosso exemplo anterior pode ser reescrito:
{$APPTYPE CONSOLE}
uses SysUtils;
type
PInteger = ^Integer;
var
p: PInteger;
begin
New(p);
try
p^ := 2;
WriteLn(Format(‘%d’, [p^]));
finally
Dispose(p);
end;
ReadLn;
end.
Atenção, após o FreeMem ou Dispose, p continua apontando para o mesmo endereço, só que este não está mais lá. Perceba que existe um risco em se usar um ponteiro, que é justamente apontar para algo que não deveria estar apontando. Uma boa prática é atribuir nil para o ponteiro que foi desalocado. Assim se por engano o ponteiro for utilizado, uma excessão do tipo EAccessViolation será lançada, detectando o problema, antes que seja tarde
Dispose(p);
p := nil;
end;
Humm legal mas eu sempre quis saber… Mas issofaz com que use menos processo do processador ?
Não, a utilização de ponteiros não melhora a performance.