As variáveis ditas “normais”, têm uma característica comum, são entidades estáticas no que concerne à memória e são alojadas na stack memory.
Uma variável dinâmica é criada (na heap memory) e destruída durante a execução do programa.
A variável é referenciada por um apontador que aponta para um local da memória, isto é, enquanto uma variável pode ser alocada em qualquer local da memória que para nós é indiferente, a variável do tipo apontador vai apontar para um local específico da memória.
A título de exemplo, imaginem uma televisão na horizontal. Se colocar-mos em cima do ecrã um copo (a nossa variável) com uma bola dentro (conteúdo da variável) nós vimos sempre a bola. Podemos trocar a bola por outro objecto qualquer que caiba no copo. Se en vez de um copo colocarem um rolo de cozinha (ponteiro) e olhar-mos lá para dentro vimos a imagem que no momento estiver na zona do ecrã onde se encontra o rolo de cozinha, podendo variar. É claro que este exemplo é discutível. A ideia é que o ponteiro mostra-nos a informação da zona de memória para onde ele está a apontar.
Type Nome_do_ponteiro=^Tipo_base
Todas as variáveis declaradas como sendo de um tipo dinâmico, vão apontar para dados do tipo base.
Exemplo como se podem declarar apontadores para inteiros
Type IntPtr=^Integer; Var V1: IntPtr;
No caso de querem 3 variaveis do tipo IntPtr declarariam assim:
Var V1, V2, V3: IntPtr;
Existem dois procedimentos standard em Pascal que permitem criar e destruir variáveis dinâmicas:
Aproveitando o exemplo acima, vamos criar, atribuir um valor e destruir a variável
begin Write('Indique um número: '); New(v1); Readln(v1^); V1^ := v1^ + 5; Write(' O seu número adicionado de 5 unidades: ',V1^); Dispose(v1); end.
De realçar que os apontadores apenas levam ^ para se aceder à informação por ele apontada.
Poderão dizer pelo que até agora que os ponteiros são iguais às variáveis, só que com mais trabalho…. Efectivamente nesta primeira parte sim, mas o melhor vem a seguir.
Quem já não teve de declarar um array enorme e mesmo assim pensar que, numa situação particular qualquer pode não chegar? Pois é, com os ponteiros podemos criar listas ligadas sem nos preocupar-mos com o tamanho. É só alocar espaço para o próximo “indice”… haja memória, claro! Para o fazermos, temos que recorrer a um tipo composto: os records Neste record, colocamos um tipo especial… o apontador Assim teríamos na declaração:
Type Ptr = ^Rec; Rec = record Marca: string[25]; Tamanho: Integer; NextPtr: Ptr; {Serve para nos indicar o campo seguinte. Se for o último deve apontar para NIL} End; Var Inicio, NovoReg , Aux1, RegAnterior: IntPtr;
Continuando, Quando se trata de construir listas ligadas ordenadas temos de ter em atenção 3 aspectos:
Inserindo um Registo B
Então pegando na declaração anterior, vamos inserir um registo numa lista ligada ordenada pela marca.
Procedure le_insere_reg; Begin New(NovoReg); Write('Marca do tubo: '); readln(NovoReg^.marca); Write('Tamanho: '); readln(NovoReg^.tamanho); Aux1:=Inicio; if (Incio=NIL) OR (NovoReg^.marca<Aux1^.marca) then begin {1º elemento da lista se a lista está vazia, ou se marca menor que o primeiro elemento da lista} NovoReg^.NextPtr:=Inicio; Inicio:=NovoReg {NovoReg Não leva ^ porque queremos que Inicio aponte para a zona de memória onde está o NovoReg e não o seu conteúdo} end else begin {Vamos percorrer a linha que tem inicio em Inicio e não queremos perder a referencia de memória de Inicio para o 1º registo da lista, então usa-se uma variável auxiliar atribuida anteriormente em Aux1:=Inicio;} While (NovoReg^.marca>=Aux1^.marca) AND (Aux1<>NIL) do begin RegAnterior:=Aux1; Aux1:=Aux1^.NextPtr; end; NovoReg^.NextPtr:=Aux1; RegAnterior^.NextPtr:=NovoReg; end; end;
Foi este principio que me fez passar de um tempo de listagem de 3 Horas para 3 minutos!
Ainda havia mais para dizer acerca das listas ligadas tal como listas duplamente ligadas, listas sem fim em que o ultimo está ligado ao primeiro, mas o essencial está aqui.