Você está na página 1de 19

DevMedia - Verso para impresso

Ponteiros
Porque precisamos e o que podemos fazer com eles

Vitor Luiz Rubio


Analista de Sistemas Sr. na Editora Revista dos Tribunais. Trabalha com Delphi desde a verso 3.
Formado em Processamento de Dados pela FATEC-SP

Do que trata o artigo


Neste artigo abordaremos ponteiros: sua sintaxe atual e mudanas ao longo das verses do
Delphi, como e onde utiliz-los e sua evoluo. Tambm abordaremos porque ainda hoje
importante conhecermos ponteiros, quais tipos usados so classificados como ponteiros e
como funciona a alocao dinmica de memria.

Para que serve


Para programar usando a API do Windows, desenvolver componentes, integrar com sistemas
legados ou feitos em outras linguagens. Os ponteiros so a base para a alocao dinmica de
memria, e consequentemente os ambientes gerenciados trazem, em seu cdigo fonte, um
extenso uso de ponteiros.

Em que situao o tema til


Mesmo no Windows 7 a sua API ainda contm vrios mtodos cujos argumentos ou resultados
so ponteiros. E neste nvel dificilmente teremos programao orientada a objetos pura, ou um
ambiente gerenciado. Estamos falando de um nvel intermedirio entre a sua aplicao e o
sistema operacional. Sempre que tivermos um mtodo em uma DLL escrita em C ou C++, o
uso de ponteiros se far necessrio para que possamos passar variveis por referncia.
Resumo do DevMan
Ponteiros so endereos da memria e podem ser armazenados em variveis. No so

valores, mas apontam para uma rea da memria onde h um valor armazenado. Embora
esse assunto possa parecer muito acadmico, ainda muito utilizado para se aproveitar ao
mximo a API do Windows e at se desenvolver coisas novas, como novos componentes que
fazem uso da API. Veremos neste artigo o bsico de ponteiros, como eles podem ser usados
para se passar variveis por referncia e qual sua relao com vetores.

Mesmo nas linguagens de alto nvel como Delphi, ponteiros so o assunto de nvel mais
baixo que podemos tratar. Isso porque estaremos lidando diretamente com endereos da
memria. H quem defenda que ponteiros so coisa do passado e que no devem mais ser
usados. Mas isso um engano. Uma rpida olhada no cdigo-fonte da VCL e da RTL pode
revelar o tamanho uso que as prprias bibliotecas do Delphi fazem.
E mesmo em ambientes gerenciados como Python, Java, Ruby e .NET, embora no usemos
ponteiros diretamente ou explicitamente, a mquina virtual, geralmente escrita em C/C++, faz
um extenso uso de ponteiros. No prprio Delphi, todo objeto, cuja memria alocada
dinamicamente, um ponteiro, como veremos mais adiante.
Para armazenar valores ns declaramos variveis. Declarar uma varivel significa reservar
um espao na memria onde poderemos armazenar um valor qualquer. Agora imagine que
cada rea da memria contm um endereo numrico. Ponteiros trabalham diretamente com
os prprios endereos. Ou seja, voc pode acessar um valor atravs de seu endereo na
memria em vez de utilizar uma varivel.
No Delphi utilizamos o operador ^ (circunflexo) para declarar uma varivel do tipo ponteiro.
Usamos tambm o operador @ precedendo variveis normais para obter o endereo delas.
O inverso de @ seria usar ^ depois de uma varivel-ponteiro, para que possamos acessar o
seu valor. Um ponteiro recm declarado que no aponta para nenhum lugar tem o valor nil.
Antes de iniciarmos a nossa aplicao, atente para a Figura 1 que exibe um exemplo do
funcionamento de um ponteiro.
Vamos desenvolver uma aplicao Delphi e ver na prtica como tudo isso funciona (no Delphi
crie um projeto Win32).

Criaremos um boto para cada exemplo, nomeados como btEx<yy> onde yy ser o nmero
do nosso exemplo. Os captions de cada boto sero anlogos, como Ex 01, Ex 02 e assim
por diante.
Modifique a propriedade Name do formulrio para frmPonteiros e informe o Caption
Trabalhando com Ponteiros, por ltimo salve sua unit como uPonteiros.pas e seu projeto
como Ponteiros.dproj.

Declarando ponteiros
No nosso primeiro exemplo vamos declarar um ponteiro para integer. Isso significa que
teremos um endereo de memria apontando para um local onde ser armazenado um valor
inteiro. Para ver como funciona o mecanismo de ponteiros iremos declarar duas variveis do
tipo integer e obter o endereo de cada uma, e posteriormente o valor. No podemos
esquecer de inicializar o nosso ponteiro com nil ou j com um endereo vlido, caso contrrio
ele vir com um endereo randmico de memria, provavelmente apontando para algum lixo
ou at mesmo para a rea de instrues.

[abrir im age m e m jane la]

Figura 1. Um ponteiro apontando para um determinado endereo na memria, que


contm o valor 2

Esquecer de inicializar ponteiros pode causar acessos indevidos a reas protegidas de


memria e at a corrupo de dados e instrues. Adicione ao formulrio o primeiro boto e
digite o cdigo da Listagem 1.
Os ponteiros que estamos usando aqui so tipados, ou seja, so ponteiros para um tipo
definido, no caso integer. As trs ltimas linhas da listagem mostram o tamanho das variveis,
em bytes. As variveis 1 e 2 so do tipo integer, por isso possuem 4 bytes. O ponteiro tambm
possui 4 bytes, porm, todo ponteiro , no fundo, um integer, e na arquitetura de 32 bits um
ponteiro tambm tem 32 bits, ou seja, 4 bytes. Experimente trocar o tipo da varivel de integer
para byte e ver que enquanto as variveis 1 e 2 tiverem 1 byte, o ponteiro continuar tendo 4
bytes.
Na Listagem 2 faremos um cdigo similar, mas desta vez mudaremos o valor da varivel e
verificaremos qual ser o valor para o qual o ponteiro aponta. Novamente insira um boto no
formulrio para a incluso deste novo cdigo.
Na Listagem 2 podemos ver que tanto faz referenciar um local na memria atravs de uma
varivel ou de um ponteiro, ambos apontaro para o mesmo local, e caso se mude esse valor,
o valor atual ser visvel em quaisquer ponteiros que apontem para ele. Podemos ter vrios
ponteiros apontando ou referenciando um mesmo lugar na memria.
Listagem 1. Declarao de ponteiros
procedure TfrmPonteiros.btEx01Click
(Sender: TObject);
var
variavel1: integer;
variavel2: integer;
ponteiro: ^integer;
begin
//Atribuindo um valor s variveis
variavel1 := 123;
variavel2 := 246;
//inicializando o ponteiro com nil
ponteiro := nil;
if ponteiro = nil then
ShowMessage(O ponteiro ainda no
aponta para lugar nenhum);
ShowMessage(O endereo de memria
da varivel 1 : +
inttostr(integer(@variavel1)) );
ShowMessage(O endereo de memria
da varivel 2 : +
inttostr(integer(@variavel2)) );
ponteiro := @variavel1;
ShowMessage(O endereo
atual do ponteiro :
+ inttostr(integer(ponteiro)) );
ShowMessage(O valor que ele aponta :
+ inttostr(integer(ponteiro^)) );
ponteiro := @variavel2;
ShowMessage(O endereo atual do ponteiro : +
inttostr(integer(ponteiro)) );
ShowMessage(O valor que ele aponta :
+ inttostr(integer(ponteiro^)) );
//mostrando os tamanhos
ShowMessage(O tamanho da variavel 1 :
+ inttostr(sizeof(variavel1)) );
ShowMessage(O tamanho da variavel 2 :
+ inttostr(sizeof(variavel2)) );
ShowMessage(O tamanho do ponteiro :
+ inttostr(sizeof(ponteiro)) );
end;

Listagem 2. Operaes com os valores apontados


procedure TfrmPonteiros.btEx02Click
(Sender: TObject);
var
variavel: integer;
ponteiro: ^integer;
begin
variavel := 50;
ShowMessage(Valor da varivel:
+ IntToStr(variavel));
//atribuindo ao ponteiro o endereo da varivel
ponteiro := @variavel;
ShowMessage(Valor apontado pelo ponteiro:
+ IntToStr(ponteiro^));
variavel := variavel + 10;
ShowMessage(Valor apontado pelo
ponteiro aps a soma: +
IntToStr(ponteiro^));
ponteiro^ := ponteiro^ + 5;
ShowMessage(Valor atual da varivel:
+ IntToStr(variavel));
end;

Ponteiros e a passagem de parmetros


Como sabemos, o Delphi possui a palavra reservada var para se passar a um mtodo
parmetros por referncia. Isso significa que no ser usada dentro do mtodo uma cpia da
varivel, mas sim a prpria varivel, podendo ter seu valor alterado.
Crie os mtodos da Listagem 3 e em seguida a chamada a eles no boto 3. Como voc pode
notar, o cdigo do boto 3 mostra como uma varivel pode ter seu valor alterado ao ser
passada a um mtodo por referncia.
A linguagem C no possui nenhuma palavra reservada para passagem de parmetros por
referncia. Uma varivel ou estrutura passada a um mtodo seria inteiramente copiada dentro
do escopo local do mtodo. Uma prtica muito comum no C/C++ para a passagem de
parmetros por referncia passar um ponteiro para a varivel. Dentro do mtodo ser
alterada a rea para onde esse ponteiro aponta.
Parece redundncia dizer isso, mas no C, voc no passa uma varivel por referncia, e sim
uma referncia (ponteiro) a uma varivel. A diferena est na sintaxe, mas na prtica a
mesma coisa.O terceiro mtodo da Listagem 3 mostra como a passagem de um ponteiro a um
mtodo pode ser equivalente passagem de uma varivel por referncia.
Listagem 3. Relao entre ponteiros e passagem de argumentos por referncia
procedure IncrementaPorValor
(i: integer);
begin
i := i + 1;
ShowMessage(Valor da varivel dentro
do mtodo :+IntToStr(i));
end;
procedure IncrementaPorReferencia
(var i: integer);
begin
i := i + 1;
ShowMessage(Valor da varivel
dentro do mtodo :+IntToStr(i));
end;
procedure IncrementaComPonteiro
(i: PInteger);
begin
i^ := i^ + 1;
ShowMessage(Valor da varivel
dentro do mtodo :+IntToStr(i^));
end;
procedure TfrmPonteiros.btEx03Click
(Sender: TObject);
var
x,y,z: integer;
begin x := 11;
y := 22;
z := 33;
ShowMessage(Valor da varivel x :+IntToStr(x));
ShowMessage(Valor da varivel y :+IntToStr(y));
ShowMessage(Valor da varivel z :+IntToStr(z));
IncrementaPorValor(x);

//sabemos que no funcionar


IncrementaPorReferencia(y);
//o mtodo mais conhecido por ns
IncrementaComPonteiro(@z);
//passando o endereo de z, @z
ShowMessage(Valor da varivel x :
+IntToStr(x));
ShowMessage(Valor da varivel y :
+IntToStr(y));
ShowMessage(Valor da varivel z :
+IntToStr(z));
end;

Nota
Para usar Watches clique em View > Debug Windows > Watches e a janela de Watches ir
aparecer. Essa janela muito importante para o debug porque com ela voc pode
visualizar os valores de variveis durante o processo de debug. Para isso, basta copiar e
colar uma varivel para dentro da janela ou, com um duplo clique na janela, escrever o
nome da varivel.

Na execuo dos mtodos mostraremos mensagens exibindo o valor das variveis em cada
etapa. Voc pode usar um memo para isso, ou, mais corretamente, uma Watch (janela do
debugger, veja Nota do DevMan).
Como no podemos declarar dentro dos parmetros de um mtodo um ponteiro usando
explicitamente o operador ^, declaramos o parmetro como sendo do tipo PInteger. O PInteger
nada mais do que um ponteiro para integer, definido em System.pas como:
PInteger = ^Integer;
Nota: Voc pode inclusive definir um Type, chamado PMyInteger = ^Integer.
Feito isso podemos usar, para alimentar esse parmetro, qualquer endereo de um integer
obtido com o operador @.
Isso muito importante caso se usem DLLs feitas em outras linguagens, onde sero
passados valores esperando que sejam modificados. A passagem de ponteiros por referncia,
usando-se a palavra reservada var, pode ser perigosa, pois cria na verdade uma referncia
duplamente indireta, ou um ponteiro para ponteiro.

Isso pode permitir que os endereos de alguns ponteiros sejam trocados, fazendo com que
apontem para outros valores, regies invlidas da memria ou para lugar nenhum.
Como era esperado o mtodo IncrementaPorValor incrementar a varivel y apenas dentro do
mtodo, e no fora dele.
Ao se trabalhar com estruturas de dados muito grandes, ou grandes vetores e matrizes, a
passagem de parmetros por referncia muito mais rpida, pois os dados no so copiados
para uma varivel interna do mtodo. Mas assegure-se de fazer isso apenas em
procedimentos que no alterem o valor ou valores da estrutura.
No estudo ou desenvolvimento de uma linguagem muito importante saber sobre o efeito
das palavras reservadas usadas nos argumentos de mtodos, e como esses argumentos so
empilhados, onde a memria alocada etc. Isso o funcionamento interno de qualquer
linguagem que venhamos a trabalhar.

Nota

Por isso nossos mtodos no devem aceitar objetos por referncia, a menos que eles
sejam criados ou destrudos por esses mtodos, como no caso do mtodo FreeAndNil, que
alm de destruir um objeto atribui nil a sua varivel.Como veremos mais adiante, esse o
mistrio envolvendo os objetos: suas variveis so ponteiros e apontam para um objeto
criado na memria. Porm nada, nenhuma varivel, o objeto propriamente dito.

Ponteiros e arrays
Arrays no Delphi nada mais so do que ponteiros para um primeiro elemento de uma srie de
tamanho finita. Para provar isso vamos declarar um array esttico global, de 10 posies, do
tipo integer. Os cdigos da Listagem 4 e Listagem 5 mostraro como o ponteiro para um array
na verdade o ponteiro para o primeiro elemento deste e mostra como podemos acessar os
elementos de um array atravs de seu ponteiro.
Tambm veremos um pouco sobre aritmtica de ponteiros. Podemos incrementar,
decrementar ou somar valores em ponteiros para avanar ou retroceder n elementos na
memria. As procedures inc e dec podem ser teis para incrementar ou decrementar um
ponteiro. Essas procedures j verificam de que tipo o ponteiro e somam o nmero de bytes
necessrios para avanar ou retroceder um elemento.
Isso importante, porque embora possamos converter um ponteiro para um integer, no
basta apenas somar ou subtrair unidades para avanar ou retroceder elementos.
Deve-se somar ou subtrair o nmero de bytes correspondente ao tamanho do tipo de dado
que estamos manipulando. Por exemplo, se estivermos trabalhando com byte podemos somar
ou subtrair 1.
Listagem 4. Relao entre ponteiros e vetores
var
frmPonteiros: TfrmPonteiros;
vetor: array[1..10] of integer;
implementation
procedure InicializaVetor();
var
I: Integer;
begin
//preencheremos os 10 valores do Vetor
//com a tabuada do 5, de 5 a 50
for I := 1 to 10 do
vetor[I] := i*5;
end;
procedure TfrmPonteiros.btEx04Click
(Sender: TObject);
var
p: PInteger;
begin
InicializaVetor();
p := @vetor;
showmessage(inttostr(p^));
//elemento 1 - valor 5
inc(p);
showmessage(inttostr(p^));
//elemento 2 - valor 10
inc(p, 2);
showmessage(inttostr(p^));
//elemento 4 - valor 20
p := pointer(integer
(p)+SizeOf(integer));
showmessage(inttostr(p^));
//elemento 5 - valor 25
p := pointer(integer(p)+SizeOf
(integer)*2);
showmessage(inttostr(p^));
//elemento 7 - valor 35
p := pointer(integer(p)+SizeOf
(integer)*5);
showmessage(inttostr(p^));
//elemento 12 - valor ???
end;

Listagem 5. Relao entre ponteiros e matrizes


var
MatrizBiDimensional: array[1..3, 1..4]
of integer;
implementation
procedure InicializaMatrizBiDimensional();
var
i,j: integer;
begin
for I := 1 to 3 do
for j := 1 to 4 do
MatrizBiDimensional[i,j] := i*j;
end;
procedure TfrmPonteiros.btEx05Click
(Sender: TObject);
var
p: PInteger;
i: integer;
begin
InicializaMatrizBiDimensional();
p := @MatrizBiDimensional;
for I := 1 to 12 do
begin
showmessage(inttostr(p^));
//elemento 1 - valor 5
inc(p);
end;
end;

Se estivermos trabalhando com caracteres Unicode devemos somar ou subtrair 2.


Trabalhando com integers devemos somar ou subtrair 4, e assim por diante.
A Listagem 4 mostra como voc pode incrementar um ponteiro com inc ou usando uma soma
comum. importante salientar que voc pode extrapolar os limites de um array e sair
vasculhando a memria inteira, como se fosse um vetor enorme, e com isso at encontrar
endereos onde so alocados dados interessantes. Lgico que isso no deve ser feito num
programa de verdade, em produo. Isso pode causar muitos Access Violations, entre
outros problemas.
O que fizemos na Listagem 4 foi criar um vetor de 10 posies, de 1 a 10 e uma procedure
para inicializ-lo. Usamos o operador @ para obter o endereo do vetor e com isso
descobrimos que o endereo de um vetor na verdade o endereo de seu primeiro elemento,
como comentei. O Delphi se encarrega automaticamente de alocar a memria necessria para
armazenar todos os elementos. E o prprio Delphi sabe onde o vetor comea e termina, s
assim ele consegue disparar a exceo Index Out of Bounds caso voc utilize um ndice
invlido.
A cada linha somamos valores no ponteiro para avanar uma posio no array. Podemos
somar com inc ou usando a linha de cdigo:
p := pointer(integer(p)+SizeOf(integer)*<>);

Nota
Nunca assuma que o tamanho de um tipo, em bytes, conhecido. Use sempre a funo
SizeOf para retornar o tamanho e multiplicar pelo nmero de elementos que voc quer
avanar ou retroceder. Por exemplo, em verses antigas do Delphi / Pascal, um integer
tinha 16 bits, o mesmo integer hoje tem 32 bits.

Nota
Arrays multidimensionais so o que comumente conhecemos pelo conceito de Matrizes. Ao

contrrio dos arrays comuns (vetores) que possuem apenas uma direo e acesso
simplificado atravs de um ndice, determinados pela linha, os arrays multidimensionais
trabalham com linhas e colunas, neste caso necessitando de mais de um ndice para
acesso de seus valores.

Isso faz com que convertamos p para integer e somemos o valor do tamanho de um integer
multiplicado pelo nmero de elementos que desejamos navegar. O inteiro resultante ns
convertemos de volta para ponteiro.
muito importante no somar ao ponteiro nmeros quebrados. Por exemplo, se estamos
trabalhando com integers no podemos somar 1 ao ponteiro. Isso far com que o ponteiro se
desloque apenas um byte e tente obter um valor inteiro de uma regio na memria que pega o
byte mais significativo de um inteiro com os trs bytes menos significativos de outro. Isso
resultar num nmero enorme, e totalmente incorreto.Temos outros tipos de arrays que
tambm devem ser observados. Os arrays multidimensionais, ou matrizes, so
multidimensionais apenas do ponto de vista do programador.
Do ponto de vista do sistema todos os arrays tm uma nica dimenso porque a memria tem
uma nica dimenso. A memria um gigantesco array unidimensional. Na Listagem 5 vamos
criar um array de duas dimenses e percorr-lo atravs de um ponteiro.

Na Figura 2 mostramos a varivel MatrizBiDimensional na Watch List. Repare que um vetor


de 3 linhas e 4 colunas de inteiros transformado em trs vetores de 4 posies cada um.
Podemos ver tambm o endereo atual de p e o valor, no caso 2, porque essa imagem foi
extrada durante a segunda iterao do lao for.
Como sabemos de antemo que nossa matriz de duas dimenses tem doze elementos, afinal,
3 x 4 = 12, o que fizemos foi obter o ponteiro para o primeiro elemento e varrer linearmente
essa regio da memria. Usamos o mtodo inc para incrementar corretamente os ponteiros.

Relao entre ponteiros, ShortStrings e


PChar
No podemos falar desse assunto sem antes retroceder um pouco no tempo. Nos primrdios
da programao Pascal/Delphi as strings poderiam ter um tamanho mximo de 255 caracteres,
e eram formadas por caracteres ASCII, ou seja, caracteres de um byte.
Conforme a evoluo do Object Pascal / Delphi Language, surgiram as strings de tamanho
infinito.
[abrir im age m e m jane la]

Figura 2. A matriz, o endereo na memria e o valor armazenado no endereo

Na verdade a capacidade desse tipo de string no realmente infinita, mas o mximo nmero
de caracteres que ela pode ter corresponde ao mximo de um inteiro sem sinal. Um pouco
mais de quatro bilhes de caracteres.
Nas ltimas verses do Delphi as strings padro passaram a ser strings Unicode de dois bytes.
Ainda existem as strings de caracteres de um byte. So as AnsiStrings, formadas por
AnsiChars. PChar (PAnsiChar no nosso caso) muito usado para se comunicar com a API do
Windows porque trata-se de uma string aberta terminada em #0 (como no C/C++).
Na linguagem C uma string na verdade um ponteiro para char, criando um vetor de
tamanho indeterminado. Como fazer para saber onde a string termina e qual o seu tamanho?
As strings no C so terminadas por um caractere nulo, #0 para ASCII ou #0#0 para Unicode.
Esses ponteiros devem ter a sua memria alocada dinamicamente e o ltimo caractere deve
ser um #0. H uma outra maneira de criar strings desse tipo que usando arrays de chars.
Na Listagem 6 e Listagem 7 vamos criar shortstrings, que so aquelas strings que usvamos
antigamente no tempo do Pascal, com tamanho limitado. Usaremos tambm vetores de Char e
ponteiros e faremos chamadas a alguns mtodos da API do Windows.
Listagem 6. Relao entre ponteiros e shortstrings
procedure TfrmPonteiros.btEx06Click
(Sender: TObject);
var
//shortstrings j so formadas
//por AnsiChar, por padro
UmaString: string[20];
//como o novo Char agora WideChar,
//usamos AnsiChar explicitamente
UmVetor: array[1..20] of AnsiChar;
h: HWND;
PonteiroChar: PAnsiChar;
//Equivale a ^AnsiChar
begin
UmaString := ClubeDelphi;
UmVetor[1] := D;
UmVetor[2] := e;
UmVetor[3] := v;
UmVetor[4] := M;
UmVetor[5] := e;
UmVetor[6] := d;
UmVetor[7] := i;
UmVetor[8] := a;
UmVetor[9] := #0;
h := FindWindowA
(nil,Trabalhando com Ponteiros);
//teste tambm com Calculadora
SetWindowTextA(h, UmVetor);
ShowMessage(Repare que o caption
desta janela mudou para: +
AnsiString(UmVetor) );
SetWindowTextA(h, UmaString);
ShowMessage(Repare que o caption
desta janela mudou para: +
UmaString ); PonteiroChar := @UmVetor;
SetWindowTextA(h, PonteiroChar);
ShowMessage(Repare que o caption
desta janela mudou para: +
PonteiroChar );
end;

O que fizemos na Listagem 6 foi criar uma ShortString (string de tamanho definido) de 20
caracteres com o contedo ClubeDelphi. Criamos tambm um vetor de chars com os nove
primeiros elementos formando a palavra DevMedia e terminando com um #0. Mostramos como
podemos usar livremente tanto a string quanto o vetor ao chamar a funo SetWindowTextA
da API do Windows. Essa funo muda o texto de uma janela encontrada, dado o seu handle.
Encontramos o handle de nossa janela com a funo FindWindowA e passando como
argumento o Caption do nosso prprio form. Podemos tambm usar o ponteiro para o vetor.

Nota
Handle, na API do Windows, um nmero que aponta para uma janela, controle, arquivo,

objeto. Imagine um Handle como se fosse um tipo TObject do Delphi, ou Pointer, ou seja,
pode apontar para qualquer coisa.

Uma questo interessante que embora o tipo PAnsiChar seja definido como sendo
^AnsiChar, voc no pode criar um ponteiro do tipo ^AnsiChar e us-lo no mtodo
SetWindowTextA, porque ela foi definida, em seu cabealho, como usando PAnsiChar, e o
Delphi considera PAnsiChar um tipo, e controla rigidamente isso.
Listagem 7. Obtendo o primeiro byte e o valor armazenado nele
procedure TfrmPonteiros.btEx07Click
(Sender: TObject);
var
UmaString: string[20];
comprimento: integer;
begin
UmaString := ClubeDelphi;
comprimento := PByte(@UmaString)^;
ShowMessage
(Comprimento usando o primeiro byte: +
IntToStr(comprimento));
ShowMessage
(Comprimento usando Length:
+ IntToStr(Length(UmaString)));
UmaString := *Clube Delphi*;
comprimento := PByte(@UmaString)^;
ShowMessage
(Comprimento usando o primeiro byte: +
IntToStr(comprimento));
ShowMessage
(Comprimento usando Length:
+ IntToStr(Length(UmaString)));
UmaString := Delphi;
comprimento := PByte(@UmaString)^;
ShowMessage
(Comprimento usando o primeiro byte: +
IntToStr(comprimento));
ShowMessage
(Comprimento usando Length:
+ IntToStr(Length(UmaString)));
ShowMessage
(Tamanho total usando SizeOf: +
IntToStr(SizeOf(UmaString)));
end;

Tudo o que testamos aqui para AnsiChar e PAnsiChar vale tambm para Char e PChar, mas
como no Delphi o tipo Char agora Unicode, a API usada deveria ser SetWindowTextW ou
apenas seu alias SetWindowText, sem o A no final, que corresponde a AnsiChar.
Um aspecto interessante sobre as ShortStrings, ou strings de tamanho fixo, que antes do
primeiro char, em sua posio zero, armazenado o tamanho da string. O Delphi gerencia
automaticamente a cpia de strings, a limitao do tamanho e a concatenao do #0 no final.
Quando usamos, concatenamos, exibimos ou atribumos strings o Delphi conta e armazena a
quantidade de caracteres no primeiro byte (ou Word) e armazena os caracteres nas posies
subsequentes. O compilador faz com que esse primeiro byte fique invisvel para ns e opera
apenas com os posteriores. Precisamos de ponteiros para ver o primeiro byte.
Obter o tamanho de uma string assim mais rpido do que contar seus caracteres at
encontrar um #0, e assim que a funo Length funciona internamente. Inclusive, o valor que
voc vai obter nesse byte corresponde ao que a funo Length retornaria.
Para provar que uma string desse tipo um vetor, usamos a funo SizeOf, para verificar seu
tamanho em bytes, e a funo retorna 21, porque a string tem 20 caracteres (bytes) mais o
primeiro byte que o contador de caracteres.
O cdigo da Listagem 7 mostrar como obter o tamanho de uma string usando seu endereo,
apontando para o primeiro elemento e convertendo para byte. Usamos aqui um PByte que
nada mais do que um ^Byte.

Cada vez que alteramos a string obtemos novamente o valor armazenado no primeiro byte,
apontado pelo prprio endereo da string. Uma outra maneira de se fazer isso como

mostrado na Listagem 8, porm no colocaremos um boto no formulrio para fazer dessa


forma.

Listagem 8. Outra maneira, usando diretamente o ponteiro


procedure TfrmPonteiros.btEx07Click
(Sender: TObject);
var
UmaString: string[20];
pcomprimento: PByte;
begin
pcomprimento := @UmaString;
UmaString := ClubeDelphi;
ShowMessage(Comprimento
usando o primeiro byte: +
IntToStr(pcomprimento^));
ShowMessage(Comprimento usando Length:
+ IntToStr(Length(UmaString)));
UmaString := *Clube Delphi*;
ShowMessage(Comprimento usando
o primeiro byte: +
IntToStr(pcomprimento^));
ShowMessage(Comprimento usando Length:
+ IntToStr(Length(UmaString)));
UmaString := Delphi;
ShowMessage(Comprimento usando o primeiro byte: +
IntToStr(pcomprimento^));
ShowMessage(Comprimento usando Length:
+ IntToStr(Length(UmaString)));
ShowMessage(Tamanho total usando SizeOf: +
IntToStr(SizeOf(UmaString)));
end;

Strings comuns
As strings comuns so uma estrutura de dados muito especial que o Delphi trata de
maneira nica. Primeiro de tudo as strings normais so todas ponteiros. Voc no pode
simplesmente medir o tamanho delas, apenas o comprimento.
Enquanto as strings de tamanho fixo j tm seu tamanho mximo alocado na Stack, as strings
de tamanho varivel so alocadas dinamicamente, conforme necessrio. Alm disso, os oito
bytes anteriores ao primeiro caractere, ou seja, offset do -7 ao 0 podem ser divididos entre
dois integers de quatro bytes cada um. Um deles armazena a quantidade de caracteres, ou
seja, seu comprimento. O outro armazena uma contagem de referncias.
[abrir im age m e m jane la]

Figura 3. Estrutura interna de uma string

Sim, strings so Reference-Counted assim como interfaces, e seu mecanismo controlado


nas partes mais internas do Delphi. Isso necessrio para tornar a cpia de strings muito
mais rpida.
Por exemplo, se voc tem duas strings, StringA e StringB, sendo que StringA tem o valor
Clube, quando voc fizer a atribuio StringB := StringA no ser copiado o inteiro valor da
string, cada caractere, porque a string pode ser enorme. copiado apenas o endereo do
primeiro byte, e o contador de referncias incrementado.
Se uma das duas strings modificada, por exemplo com StringB := StringB + Delphi, nesse
caso o contador de referncias decrementado, a string verdadeiramente copiada byte a
byte e o valor Delphi concatenado ao final, incrementando o tamanho da mesma. A Figura
3 ilustra a estrutura interna de uma string.
Se pudssemos usar um Record para representar uma string ele seria mais ou menos como
na Listagem 9.

No vamos nos preocupar com o cdigo da Listagem 9, porque ele no representa realmente
a estrutura das strings, apenas uma ilustrao ou metfora de como seria. No mundo real a
parte de caracteres seria apenas um ponteiro para uma outra estrutura que pudesse
armazenar MaxInt-8 caracteres.
Isso porque como j dissemos, as strings no tm um tamanho de dois bilhes de bytes. Esse
seu tamanho mximo, porm o seu tamanho atual pode aumentar mediante alocao
dinmica de memria, gerenciada automaticamente pelo Delphi.
O -8 est aqui justamente porque os primeiros 8 bytes so reservados para o contador de
referncias e o de caracteres, respectivamente.
No cdigo da Listagem 10 vamos explorar esses contadores de referncias e de caracteres e
vamos mostrar como uma string j um ponteiro, tratado de maneira diferente pelo Delphi.

Na Listagem 10 criamos cinco strings. Trs delas tiveram valores constantes, strClube :=
Clube, strDelphi := Delphi e strTitulo := - Ponteiros. J a StringA formada dinamicamente,
em RunTime, pela concatenao dessas trs.
Listagem 9. Representao de como seria a estrutura de uma string
TEstruturaString = record
Referencias: integer;
Comprimento: integer;
Caracteres: array[1..MaxInt-8] of Char;

end;

Listagem 10. Obtendo o contador de referncias e o de caracteres de uma string


procedure TfrmPonteiros.btEx08Click
(Sender: TObject);
var
StringA, StringB, strClube,
strDelphi, strTitulo: string;
Referencias, Comprimento: PInteger;
begin
strClube := Clube;
strDelphi := Delphi;
strTitulo := - Ponteiros;
StringA := strClube+strDelphi+strTitulo;
ShowMessage(A string tem o comprimento de
+ IntToStr(Length(StringA))+
Porm sua varivel mede apenas
+ IntToStr(SizeOf(StringA)) +
Bytes);
Referencias := PInteger
( integer(StringA)-8);
Comprimento := PInteger
( integer(StringA)-4);
ShowMessage(A string tem o comprimento de
+ IntToStr(Comprimento^)+
E foi referenciada
+ IntToStr(Referencias^) + vezes);
Referencias := PInteger
( integer(StringA)-8);
Comprimento := PInteger
( integer(StringA)-4);
//criamos mais uma referncia
StringB := StringA;
ShowMessage(A string tem o comprimento
de + IntToStr(Comprimento^)+
E foi referenciada + IntToStr
(Referencias^) + vezes);
//modificamos a primeira string,
//atribuindo o valor da segunda e
//concatenando algo
StringA := StringB +
-Teste de Referncia;
Referencias := PInteger
( integer(StringA)-8);
Comprimento := PInteger
( integer(StringA)-4);
ShowMessage(A string tem o comprimento
de + IntToStr(Comprimento^)+
E foi referenciada + IntToStr
(Referencias^) + vezes);
end;

Isso importante porque para uma string com valor constante o contador de referncias
sempre -1, que um valor reservado especial do Delphi. Sempre que uma string uma

constante, vem de uma constante ou de uma constante literal, como no caso strClube :=
Clube, o valor do contador de referncias ser -1. Se quiser pode fazer o teste com a
strClube.
Ao concatenar as trs strings atribuindo o resultado dinmico em StringA, esta passa a ficar
com 23 caracteres, e o contador de referncias passa a ser 1, j que tem apenas uma
referncia a esse valor.
A cada iterao atualizamos os ponteiros para ter certeza de que estamos apontando para o
endereo correto, j que o Delphi pode remanejar os endereos e tamanhos ocupados pelas
strings.Quando fazemos StringB := StringA as duas passam a ser exatamente idnticas. Por
esse motivo, no so copiados os valores dos 23 caracteres para a StringB. A StringB passa
a apontar para o mesmo endereo de memria que a string A e seu contador de referncias
incrementado para 2, j que agora existem duas strings apontando para o mesmo endereo.
Quando modificamos qualquer uma das duas, o contador de referncias volta a diminuir e os
caracteres so atualizados.
Tudo isso faz com que o Delphi tenha uma excelente performance ao lidar com
concatenaes de strings, superando em muito as linguagens gerenciadas, mas com um
recurso de contagem de referncias e alocao dinmica de strings que voc s encontraria
em linguagens gerenciadas/interpretadas.
Isso torna o Delphi robusto e rpido, e muito mais fcil de usar do que o C/C++ onde a
alocao de espao em memria para as strings manual, alm das cpias serem feitas
atravs de mtodos globais de bibliotecas, e todos os caracteres serem copiados.
Para se ter uma ideia de como o Delphi eficiente, comparando com o ambiente .Net, no .Net
Framework 1.1, por exemplo, cada concatenao de strings resulta na destruio de uma
string e na criao de outra, com o contedo completo. Isso resulta num grande overhead
quando se estiver criando strings grandes, como arquivos XML/HTML ou grandes textos.

Tanto que para textos grandes a Microsoft recomenda usar-se a classe StringBuilder em vez
de strings (algo como a TStringList da VCL). A concatenao de strings dentro de um loop, no
.Net, seria realmente muito mais lenta que usando o Delphi.

Nota
Se voc tentar usar o mtodo SizeOf em uma string comum (de tamanho varivel)
receber como resposta apenas o valor 4. Isso ocorre porque uma string um ponteiro,
possuindo apenas 4 bytes.

Ponteiros para mtodos


Um caso muito interessante onde podem ser usados ponteiros refere-se aos ponteiros para
mtodos. Um exemplo prtico so as impressoras fiscais. Cada impressora vem com um
conjunto de DLLs que deve ser usado para chamar as funcionalidades da impressora. O
problema que voc no pode linkar seu software rigidamente, ou estaticamente com todas
essas DLLs, muito menos com as DLLs de uma empresa s, se voc quiser criar um sistema
flexvel.
Embora os nomes das funes nas DLLs sejam ligeiramente diferentes, e alguns fabricantes
tenham mais funes do que outros, o conjunto bsico de funes, imposto pelo Fisco, pode
ser encontrado com nomes similares em todas as DLLs.
Voc pode sim carregar as DLLs e mtodos dinamicamente, dependendo de um arquivo de
configurao.

Essa a maneira recomendada de se fazer isso, mas mesmo que voc defina estaticamente
os cabealhos ou interfaces das funes dentro das DLLs voc pode criar um conjunto seu
de funes, que so na verdade ponteiros para funes com uma determinada assinatura.
Usando esses ponteiros voc pode, em uma sesso initialization, dizer quais mtodos reais os
ponteiros apontaro.
A Listagem 11 mostra como isso ficar na prtica. Criaremos algumas procedures e functions
para substituir outras muito conhecidas do Delphi, e criaremos alguns Types que sero na
verdade mtodos estticos globais, ou seja, procedures e functions.
Voc pode estar se perguntando por que no usamos o operador @ antes dos nomes das
funes para passar a referncia a elas para a varivel ExibeMsg. Na verdade isso seria o
correto. Se voc experimentar fazer esse cdigo em uma verso antiga do Delphi, como o
Delphi 3 por exemplo, voc ser obrigado a usar o operador @. A mesma coisa acontecer se
voc estiver usando o Lazarus.
Isso foi introduzido posteriormente no Delphi para facilitar esse tipo de atribuies indiretas
de mtodos. Algumas outras facilidades foram introduzidas no Delphi para a manipulao de
ponteiros, mas no citaremos essas, pois elas, embora tenham simplificado a sintaxe,
afastaram um pouco a linguagem dos padres do Pascal. Se voc desejar fazer um teste,
coloque os operadores @ antes dos nomes dos mtodos e ver que funcionar normalmente.
O que fizemos na Listagem 11 foi declarar uma varivel do tipo TMostraMensagem, que
definida por:
TMostraMensagem = procedure(const mensagem: string);
E atribumos a esta varivel duas procedures com a mesma assinatura, e que fazem
praticamente a mesma coisa.

Uma a ShowMessage original do Delphi, da unit Dialogs, e outra que ns mesmos criamos,
que internamente usa MessageBox para criar uma mensagem com cone e ttulo.
Desde a verso 2009, o Delphi tem uma maneira elegante de criar referncias a mtodos,
que so chamados de mtodos annimos. Eles podem ser usados em conjunto com ponteiros
para mtodos, conforme a Listagem 12. As palavras chaves reference to tambm podem ser
usadas para criar uma referncia (ponteiro) para mtodo. O que vemos adicionalmente um
mtodo annimo criado dentro do cdigo do evento OnClick do boto.
O cdigo da Listagem 12 praticamente idntico ao cdigo da Listagem 11, exceto pelo tipo
que declaramos e usamos:
TMostraMensagemAnonima = reference to procedure(const mensagem: string)
Com esse cdigo criamos um tipo que um mtodo e podemos tanto usar referncias
(ponteiros) a mtodos como criar mtodos InLine, os novos mtodos annimos do Delphi. Com
isso tambm conseguimos mostrar que vrios tipos de dados do Delphi so na verdade
ponteiros. A ltima relao que mostraremos ser a relao entre ponteiros e objetos.
Listagem 11. Ponteiros para mtodos
private
{ Private declarations }
public
{ Public declarations }
end;
TMostraMensagem = procedure
(const mensagem: string);

var(...)
procedure MensagemPersonalizada
(const mensagem: string);
begin
MessageBox(Application.Handle,
pchar(mensagem)
, Mensagem: , MB_OK or
MB_ICONERROR);
end;
procedure TfrmPonteiros.btEx09Click
(Sender: TObject);
var
ExibeMsg: TMostraMensagem;
mensagem: string;
begin
mensagem := Hello World;
//ligando a varivel ExibeMsg
//com a MensagemPersonalizada
ExibeMsg := MensagemPersonalizada;
ExibeMsg(mensagem);
//ligando a varivel ExibeMsg
//com a MensagemPersonalizada
ExibeMsg := ShowMessage;
ExibeMsg(mensagem);
end;

Listagem 12. Mtodos annimos e ponteiros


private
{ Private declarations }
public
{ Public declarations }
end;
TMostraMensagem = procedure
(const mensagem: string);
TMostraMensagemAnonima =
reference to procedure
(const mensagem: string);
var
(...)
procedure TfrmPonteiros.btEx10Click
(Sender: TObject);
var
ExibeMsg: TMostraMensagemAnonima;
mensagem: string;
begin
mensagem := Hello World;
//ligando a varivel ExibeMsg
//com a MensagemPersonalizada
ExibeMsg := MensagemPersonalizada;
ExibeMsg(mensagem);
//ligando a varivel ExibeMsg
//com a MensagemPersonalizada
ExibeMsg := ShowMessage;
ExibeMsg(mensagem);
//criando um cdigo
//inline para ExibeMsg
ExibeMsg :=
procedure
(const s: string)
begin
ShowMessage
(Mensagem Annima: + s);
end;
ExibeMsg(mensagem);
end;

Ponteiros e objetos: Hackeando


propriedades read-only
Objetos so ponteiros. Podemos ver isso claramente com o mtodo SizeOf, conforme mostra
a Listagem 13. Mas os objetos no so ponteiros comuns. So ponteiros para estruturas
complexas, com vrios nveis de direo. Utilizar o operador ^ em um ponteiro no retornaria
nenhum valor.
Conforme visto na Listagem 13, um objeto um ponteiro, e o tamanho de um ponteiro o
mesmo tamanho que os integers: 4 bytes. Porm o tamanho real da instncia de um objeto,
levando-se em conta as propriedades herdadas de TObject e as propriedades criadas por ns
pode ser vista com o mtodo InstanceSize.
Como sabemos, podemos criar propriedades Read-Only, que so definidas conforme
operaes internas das classes ou durante a construo do objeto. O prprio Delphi tem

vrias classes com propriedades Read-Only, por exemplo, o objeto Singleton Screen, da
classe TScreen contm a propriedade Pixels Per Inch que Read-Only, definida internamente
dentro de sua unit, atravs da chamada de uma funo da API do Windows.
Tentar mudar o valor de uma propriedade somente leitura seria um erro de compilao.
Porm, e se consegussemos achar um ponteiro para a propriedade, ou pelo menos para os
Fields privados internos da classe? Podemos criar um ponteiro com endereo inicial do prprio
objeto e ir somando nmeros ao endereo desse ponteiro para vasculhar onde nosso
objeto armazena os valores das propriedades.

Nota
Na verdade, se pararmos para pensar, no existe nada somente leitura na memria de um
computador.

Crie uma nova unit chamada uPessoa.pas e implemente nessa unit o cdigo da Listagem 14.
Faremos uma classe TPessoa simples, com as propriedades somente leitura Nome, Idade e
Peso.
Uma peculiaridade dessa classe que todas as propriedades so somente leitura e seus
valores so definidos no corpo do construtor. Ao criarmos um objeto desse tipo, uma vez
passados os valores, no poderemos mais alterar suas propriedades. O que vamos fazer na
Listagem 15 ser criar um ponteiro para cada propriedade, do mesmo tipo que ela, e alterar as
propriedades atravs dos ponteiros. Quebramos assim o conceito de encapsulamento de uma
classe do Delphi e expomos seus atributos internos. No se esquea de adicionar a nova unit
ao uses do frmPonteiros.
Listagem 13. Um objeto um ponteiro
procedure TfrmPonteiros.btEx11Click
(Sender: TObject);
var
pessoa: TPessoa;
begin
pessoa := TPessoa.Create
(Ken Masters, 28, 85);
ShowMessage(O tamanho do objeto, embora tendo
uma string, um integer e um double :
+ IntToStr(SizeOf(pessoa)));
ShowMessage(O tamanho real do
objeto, em bytes ocupados no heap : +
IntToStr(pessoa.InstanceSize));
pessoa.Free;
end;

Listagem 14. Classe TPessoa


unit uPessoa;
interface
type
TPessoa = class
private
FNome: string;
FIdade: integer;
FPeso: double;
public
property Nome:
string read FNome;
property Idade:
integer read FIdade;
property Peso:
double read FPeso;
constructor Create
(aNome: string; aIdade:
integer; aPeso: double);
end;
implementation
{ TPessoa }

constructor TPessoa.Create
(aNome: string; aIdade:
integer; aPeso: double);
begin
FNome := aNome;
FIdade := aIdade;
FPeso := aPeso;
end;
end.

Listagem 15. Desafiando o encapsulamento da OO, com ponteiros


procedure TfrmPonteiros.btEx12Click
(Sender: TObject);
var
pessoa: TPessoa;
pi: PInteger;
ps: PString;
pd: PDouble;
begin
pessoa := TPessoa.Create
(Ken Masters, 28, 85);
pi := @pessoa.Idade;
ps := @pessoa.Nome;
pd := @pessoa.Peso;
ShowMessage(Nome: + pessoa.Nome);
ShowMessage(Idade: + inttostr(pessoa.Idade));
ShowMessage(Peso: + FloatToStr(pessoa.Peso));
ShowMessage(Endereo do objeto: + inttostr(integer(pessoa)));
ShowMessage(Endereo do nome: + inttostr(integer(ps)));
ShowMessage(Endereo da idade: + inttostr(integer(pi)));
ShowMessage(Endereo do peso: + inttostr(integer(pd)));
//Erro: Cannot assign to a read-only property
{
pessoa.Nome := E. Honda;
pessoa.Idade := 35;
pessoa.Peso := 180;
}
pi^ := 55;
ps^ := M. Bison;
pd^ := 90;
ShowMessage(Nome: + pessoa.Nome);
ShowMessage(Idade: + inttostr(pessoa.Idade));
ShowMessage(Peso: + FloatToStr(pessoa.Peso));
pessoa.Free;
end;

Criamos uma instncia do objeto TPessoa, com o nome Ken Masters, idade 25 e peso 85.
Nada de muito extraordinrio. Criamos trs ponteiros, um para integer, um para string e outro
para double. No precisamos definir esses ponteiros, ou tipos, nem declar-los com o sinal de
^, simplesmente porque o Delphi j define seus tipos da seguinte maneira, na unit System.pas:
Type
(...)
PInteger
PDouble

= ^Integer;
= ^Double;

PString = PUnicodeString;
PUnicodeString = ^UnicodeString;

Em cada ponteiro armazenamos o endereo de uma propriedade, usando o operador @ no


prprio objeto pessoa. Ao exibirmos os endereos de cada ponteiro, inclusive do prprio
objeto, podemos reparar que cada propriedade est na memria, imediatamente distncia
equivalente ao tamanho de sua predecessora, e que todas elas esto alinhadas
consecutivamente.
Como nossas propriedades so lidas diretamente dos campos, sem passarem por um mtodo
de acesso Get, as propriedades apontam diretamente para os Fields privados. Quem faz o
controle se a propriedade somente leitura ou no so as bibliotecas do Delphi.

Com os ponteiros que criamos pudemos alterar esses valores somente leitura e alterar o
valor das propriedades.
Essa tcnica que usamos no recomendada, pois pode criar efeitos colaterais indesejados
em cascata. Fizemos essa experincia apenas como prova de conceito.
Mesmo assim, se voc tiver um daqueles componentes ou bibliotecas de terceiros, antigo, de
uma empresa que j fechou, sem fontes e sem suporte e desejar alterar alguma classe ou
propriedade, pode criar um descendente que se utilize dessa tcnica. Voc pode testar isso
removendo do projeto a unit uPessoa.pas e renomeando essa unit, mas mantendo no diretrio
do projeto a uPessoa.dcu compilada. Deixo a cargo do leitor fazer os testes em classes nas
quais as propriedades so lidas de um mtodo acessor "get"

Nota
Voc dever criar um descendente para sua classe, que promova as propriedades
somente leitura para published, para poder usar RTTI. Na seo de links voc encontrar
um post antigo do blog de Hallvard Vassbotn, colunista e editor tcnico da revista inglesa
The Delphi Magazine e autor de vrios livros.

Alocao de memria
No poderamos falar sobre ponteiros sem falar sobre a alocao dinmica de memria.
Primeiro, um aspecto terico, mas importante, que existem vrias reas de memria: a rea
Data onde ficam alocadas nossas variveis e constantes globais, se que ns ainda as
usamos. A rea Stack onde ficam armazenadas chamadas a mtodos, parmetros dos
mtodos e variveis ou arrays internos do escopo de mtodos.

1 - Sempre que alocar memria use, dentro de um finally, o mtodo correspondente para
desalocar memria, caso contrrio criar um vazamento de memria;
2 - Sempre que alocar memria para uma varivel usada por um mtodo em uma DLL, caso
aloque memria fora da DLL, desaloque tambm fora da DLL e vice-versa;
3 - Sempre que precisar passar Pchars para DLLs que apenas leem ou usam a string
passada, pode-se passar uma constante ou uma string convertida para Pchar, mas sempre
que precisar que o contedo de uma string seja modificado dentro de uma DLL e devolvido ao
Delphi, no use functions, e aloque e desaloque a memria necessria fora da DLL.
Na Listagem 16 mostramos algumas das diferentes maneiras de se alocar memria.
Na Listagem 16 podemos ver que memria alocada com new deve ser desalocada com
dispose, memria alocada com GetMem deve ser desalocada com FreeMem e memria
alocada com GetMemory (que retorna ao endereo do ponteiro) deve ser desalocada com

FreeMemory.
Existem ainda outras funes da API do Windows, como HeapAlloc e VirtualAlloc para alocar
memria no heap e na memria virtual, respectivamente. Esses casos ns no estudaremos
nesse artigo.

Cada funo para alocar memria tem sua peculiaridade. Por exemplo, new serve somente
para alocar um nico espao para um nico valor do tipo desejado, no caso do integer, 4
bytes. GetMem pode ser usado para alocar, por exemplo, 20 bytes criando assim um vetor
para 5 inteiros. importante estudar a alocao e destruio de memria para utilizar
bibliotecas de terceiros, escritas em outras linguagens, principalmente se forem bibliotecas
que controlam dispositivos de hardware especficos.
Listagem 16. Alocao dinmica de memria
procedure TfrmPonteiros.btEx13Click
(Sender: TObject);
var
a,b,e: ^integer;
begin
try
new(a);
GetMem(b, sizeof(integer));
e := GetMemory(sizeof(integer));
a^ := 5;
b^ := 10;
e^ := 100;
ShowMessage(IntToStr(a^));
ShowMessage(IntToStr(b^));
ShowMessage(IntToStr(e^));
finally
dispose(a);
FreeMem(b);
FreeMemory(e);
end;
end;

Concluso
Embora ponteiros seja um assunto bastante acadmico, e considerado at mesmo de baixo
nvel, este um assunto essencial caso se deseje interagir com a API do Windows ou com
outras APIs de outros sistemas. Comunicao com hardware especfico, atravs de
bibliotecas que se comunicam com as portas seriais ou paralelas tambm faro um uso
extenso de ponteiros. Na indstria, softwares que controlam mquinas e sensores,
termmetros e aplicaes em tempo real tambm fazem um uso extenso de ponteiros. Alm
disso, se voc programa bibliotecas e APIs para outros programadores deve ter
conhecimento sobre ponteiros e alocao de memria.
Longe de ser um tratado detalhado sobre o assunto, demos apenas uma pincelada num
assunto to amplo e que no vai ficar desatualizado enquanto os nveis e camadas mais
baixos de nossos sistemas forem escritos em C/C++ e Assembly. Esperamos que esse artigo
seja til para seus ajustes finos e uso de bibliotecas de terceiros. At a prxima.

Você também pode gostar