Ponteiros
O armazenamento de qualquer valor no computador é feito na memória. Quando algo esta sendo processado, o armazenamento dos valores referentes a este processo é feito na memória principal. Como o computador acessa esses valores? Cada lugar da memória possui um endereço, ao acessarmos um determinado endereço de memória obtemos o valor armazenado naquele momento, naquele endereço.
Quando um programador está escrevendo um programa que calcule a soma de dois valores, ele vai precisar armazenar temporariamente três valores: Determinado número "X", determinado número "Y" e o Resultado da soma.
Para fazer isso efetivamente, ele declara variáveis. O que ocorre nos bastidores quando isso é feito? O compilador reserva um espaço na memória (ou seja, um endereço) para o armazenamento de um determinado valor (o espaço vai variar de acordo com o tipo definido para a variável quando ela foi declarada). O Programador, vê a variável “Num1”, do tipo numérico inteiro que ele criou, enquanto que o compilador “entende” aquilo como um endereço da memória aonde será armazenado o valor atribuído aquela variável (esse endereço é representado por um número hexadecimal ). Posso concluir então, que é possível acessar um determinado valor armazenado direto pelo endereço dele? Certamente sim. Isso que acabei de explicar, pode não fazer nenhuma diferença para vc agora. Contudo, em determinadas situações o entendimento desse mecanismo pode fazer a diferença entre vc estar no céu ou no inferno no mundo da programação.
Existe um tipo de dado chamado de ponteiro. Ele serve para armazenar a referência de um endereço de memória de uma variável qualquer. Como assim? Ponteiro, aponta para algum lugar. Aponta para um endereço, um local na memória principal do computador. A idéia seria, na prática, o ponteiro te leva imediatamente ao endereço para o qual ele aponta.
Num primeiro momento isso pode parecer um complicação desnecessária, na medida em que agora precisamos nos preocupar não só com o valor a ser armazenado, mas também com o endereço aonde isso será feito. Mas antes de tirar conclusões precipitadas vamos nos aprofundar um pouco mais no assunto, testar alguns exemplos para experimentarmos na prática o quanto a utilização de ponteiros pode tornar seus programas muito mais poderosos.
Variáveis Estáticas: trata-se das váriaveis cujo compilador reserva espaço para elas estaticamente, no momento da linkedição. Espaço esse que por sua vez será alocado na memória stack. Por exemplo: Uma variável do tipo "Integer" (a qual chamaremos hipoteticamente de ZnInteger) que vôce declarar no seu programa, durante a excução do mesmo, obviamente, em algum momento será alocado um espaço de 32 bits para aquela variável (ZnInteger). O Compilador aloca estaticamente espaço para esse tipo de variável e o próprio se preocupa em desalocar este espaço. Exemplo:
Em Pascal -
Num1: Integer; { A é uma variável do tipo Integer, e ocupará 2 bytes na memória enquanto o programa estiver em execução }
Em C ou Java –
int Num1;
Em Pascal
procedure GetMem(var P: Pointer; Size: Integer); //(Para alocar)Ponteiros: Tipos de variáveis que apontam para um endereço de memória específico. Os Ponteiros podem apontar para:procedure FreeMem(var P: Pointer[; Size: Integer]); //(Para desalocar)
1) Valores, áreas com Dados (Exemplo: uma variável. Ou melhor o conteúdo de uma
variável.
2) Uma rotina (Procedure ou Function);
3) Endereço nulo.
Sobre as variáveis:
- tipadas - o ponteiro conhece qual o tipo de dado para o qual está apontando.
- não tipadas - o ponteiro não conhece para que tipo de dado está apontando. Nestes casos seremos obrigados a fazer um cast, um Typecast a fim de definirmos para o compilador como deve ser tratado o condeúdo do endereço que estamos acessando.Falaremos sobre Typecast mais adiante.
Para inicializar um ponteiro com o endereço de uma variável usamos o operador @(arroba). Ex.:
AuxZn := @ZnValue; { Aux recebe o endereço da variável Value }
Para inicializar um ponteiro com um novo endereço de memória, veja trecho ilustrando abaxio:
var ZnAux: ^Integer; begin (* ZnAux - aponta para um endereço de memória reservado para o tipo Integer*) new(ZnAux); end;
O que está acontecendo aqui? Foi reservado um endereço de memória e “ZnAux” está apontando para essa área.
Neste último caso o programador será responsável pela alocação, bem como pela desalocação. No primeiro caso isto não é necessário (e sequer deverá ser feito), pois o endereço de memória pertence à variável estática “ZnValue”.
Apontando Rotinas: Os ponteiros podem armazenar o endereço de mémória de um "procedure" ou "function".
Apontando para "null": A palavra reservada "nil" significa valor nulo. Trata-se de declarar um ponteiro que não aponte para nenhum endereço de memória. Exe:
ZnAuxPT := nil;
O ponteiro ZnAuxPT não está apontando para dado algum. Logo, podemos testar quando um ponteiro aponta para algum valor:
if ZnAuxPT = nil then Showmessage('Não há Valor, não ha endereço. Ou melhor, o endereçoé nulo!') else Showmessage ('ZnAuxPT o Endereço não é nulo');
Declarando ponteiros
Em pascal:
Para declarar um ponteiro, na seção "var", definimos o identificador, seguido de dois pontos (":"), o tipo para o qual ele apontará, precedido pelo operador "^" (circunflexo).
var ZnPtNum: ^Integer; { ZnPtNum aponta para um Integer } ZnPtChar: ^string; { ZnPtChar aponta para uma string} begin
Para declarar um ponteiro não tipado:
var ZnPtVar: Pointer; { ZnPtVar é uma variável ponteiro que apontar para qualquer tipo de dado }
Declarando ponteiro para uma procedure ou função:
var ZnPtProc: procedure(ZnX: String); ZnResultado: Integer; procedure ZnProcedureStr(ZnAlfa: String); begin ShowMessage('ZnProcedureStr recebeu ', x); end; function ZnFunctionSoma(ZnNun1, ZnNun2: Integer); begin ShowMessage(Format('ZnFunctionSoma somou %d com %d. O Reseultado é: %d ', [ZnNun1, ZnNun2, (ZnNun1 + ZnNun2)])); end; begin ZnPtProc := @ZnFunctionSoma; ZnResultado := ZnPtProc(2, 4); if ZnResultado = 6 then begin ZnPtProc := @ZnProcedureStr; (* Exibirá: 'Rotina 2 recebeu Estação Zn: estacaozn.blogspot.com'*) ZnPtProc("Estação Zn: estacaozn.blogspot.com"); end; end;
Endereço X Valor armazenado
É importante destacar que os ponteiros nos dão acesso a duas informações:
uma é com o endereço para o qual ela aponta
A outra é o valorarmazenado no respectivo endereço.
var ZnPtInt: ^Integer; ZnInt: Integer; begin { A linha abaixo faz com que “ZnPtInt” aponte para o endereço de ZnInt, é lida assim: "ZnPtIntrecebe o endereço de ZnInt" } ZnPtInt := @ZnInt; { primeira forma - estamos trabalhando o endereço para o qual ZnPtInt está apontando} { A linha abaixo guarda o valor 5 na área de memória apontada porZnPtInt, é lida assim: "O endereço apontado por A recebe 5 } ZnPtInt^ := 5; { segunda forma - estamos trabalhando com a informação apontada por ZnPtInt} ShowMessage(Format('O valor é: %d.', [ZnInt])); end.
Na linha 7, definimos que ZnPtInt deverá apontar para ZnInt, em seguida colocamos o valor 5 no endereço apontado por ZnPtInt
(agora ZnPtInt aponta para ZnInt), em seguida exibimos o valorarmazenado por ZnInt.
Typecast
Sobre Typecast, veja ....
Exemplo Dummy:
var ZnPtVar: Pointer; ZnNum: Integer; begin ZnPtVar := @ZnNum; ZnNum := (10 * 200); ShowMessage(Format('Estação zn! O Valor é: %d', Integer(ZnPtVar^));
Usamos o circunflexo após a variável ponteiro para poder acessar o valor apontado.
Desalocando o Ponteiro
Ao usarmos a procedure new alocamos memória para um ponteiro. O mesmo deverá ser desalocado. Para isso evocamos a procedure “dispose”. Exemplo:
procedure ZnDesaloca; var ZnPtAux: ^Integer; begin new(ZnPtAux); { Aloca memória para um Integer } { Atribui valor a ZnPtAux. O endereço de memória passa a armazenar esse valor} ZnPtAux^:= 210; dispose(Aux); { Desaloca o espaço reservado pela procedure New } end.
louvado seja a estaçãozn, que explica com uma linguagem descomplicada toda aquela parte dificil de entender nas linguagens de programação
ResponderExcluirCara... sou programador a 5 anos e nunca vi nada tão simples e claro assim. valeu, muito bom.