Revisão Ponteiros

Propaganda
Ponteiros
Professor Sérgio Furgeri
Revisão Ponteiros
Variáveis dinâmicas
Comparação entre variáveis estáticas e variáveis dinâmicas.
---------------------------------------------------------Até o presente momento, lidamos com variáveis que tiveram de ser criadas antes
de se executar um programa. São variáveis que existem o tempo todo, ou seja, são
variáveis estáticas. Portanto, a alocação de memória para esse tipo de variável
é feita antes da execução do programa. A grande desvantagem desse tipo de
variável é o fato de que uma vez criada, o espaço de memória que ela ocupa não
pode mais ser alterado. As variáveis dinâmicas podem ser criadas e ou destruídas
durante a execução de um programa, e esta, é a grande vantagem delas sobre as
estáticas. As variáveis dinâmicas podem ser obtidas através de um tipo prédefinido em Pascal, chamado Pointer. O pointer ou apontador, como o próprio nome
diz, aponta para um local de memória onde está armazenada uma variável.
O procedimento para se declarar uma variável do tipo pointer é simples:
Var
p : ^Integer;
Essa declaração apenas define uma variável do tipo pointer, que irá apontar o
endereço de uma variável dinâmica do tipo Integer. Por enquanto a variável tipo
Pointer não está apontando para nenhum lugar (NIL). É o mesmo que atribuir NIL a
variável (p:=NIL;)
Sempre que criamos uma variável do tipo pointer, ela tem o valor inicial NIL.
Criação de variáveis dinâmicas.
Para criar uma variável dinâmica é usada a procedure New, interna do Pascal. Sua
sintaxe é:
New(p);
Isto faz com que seja alocado um espaço de memória, suficiente para armazenar
uma variável do tipo associado a p, no caso integer. Esse espaço de memória fica
num local especial chamado HEAP. No caso do IBM/PC, o HEAP é toda a memória não
utilizada pelo sistema. Portanto, a declaração New(p) aloca um espaço de memória
no HEAP, suficiente para armazenar uma variável do tipo Integer e retorna o
endereço inicial desta região de memória para a variável p. Lembre-se que p é do
tipo pointer.
Para acessar o conteúdo dessa variável é usada a seguinte simbologia: p^
Para atribuir um novo endereço a variável: p:=add(i); Nesse caso p recebe o
endereço de i.
Exemplo do uso de Ponteiros:
Program Exemplo;
Uses CRT;
Type Ponteiro = ^Integer;
Var p : Ponteiro;
i : Integer;
{p é uma variável do tipo pointer que aponta para variáveis dinâmicas do tipo
integer}
Begin
ClrScr;
If p = NIL Then Writeln('A variável está sem endereço');
{como p acabou de ser criada, ela não está apontando para nenhum endereço, ou
seja, seu valor inicial deve ser NIL. Para descobrirmos se isso é verdadeiro,
basta compará-la com NIL}
New(p); {acabamos de criar uma variável dinâmica do tipo Integer, e seu
endereço foi colocado no pointer p }
Página 1
Ponteiros
Professor Sérgio Furgeri
p^:=100; {estamos atribuindo o valor 100 à variável dinâmica recém criada }
Writeln(p^);
i:=200;
p^:=i;
Writeln(p^); {será escrito 200}
{ A funçäo addr(var) retorna o endereço da variável var }
p:=addr(i); { o pointer contém agora o endereço da variável i }
p^:=1000;
Writeln(i); {Será escrito 1000}
End.
Exemplo 2: Criar um programa em Pascal que permite acessar o conteúdo da memória tanto pelo
nome da variável, quanto pelo seu endereço.
Solução:
program pram;
uses crt;
var
NUM1, NUM2 : byte;
LETRA1, LETRA2, LETRA3 : char;
PNUM1, PNUM2 : ^byte;
PLETRA1, PLETRA2, PLETRA3 : ^char;
begin
clrscr;
{acessando a memória pelos nomes}
PNUM1 := addr (NUM1); { ou PNUM1:=@NUM1 }
PNUM2 := addr (NUM2);
PLETRA1 := addr (LETRA1);
PLETRA2 := addr (LETRA2);
PLETRA3 := addr (LETRA3);
{atribuindo valores as variáveis}
NUM1 := 1;
NUM2 := 200;
LETRA1 := 'A';
LETRA2 := 'B';
LETRA3 := 'C';
{acessando a RAM pelos nomes e por seus endereços}
writeln (NUM1, '=', PNUM1^);
writeln (NUM2, '=', PNUM2^);
writeln (LETRA1, '=', PLETRA1^);
writeln (LETRA2, '=', PLETRA2^);
writeln (LETRA3, '=', PLETRA3^);
Página 2
Ponteiros
Professor Sérgio Furgeri
{escrevendo os endereços onde os conteúdos estão armazenados}
writeln (longInt(PNUM1));
writeln (longInt(PNUM2));
writeln (longInt(PLETRA1));
writeln (longInt(PLETRA2));
writeln (longInt(PLETRA3));
end.
Questão 2. Como obter os endereços reais ocupados pelas variáveis ?
R: Como vimos, basta utilizarmos a função addr. Outra maneira é utilizar o símbolo @ (at) ao
lado esquerdo do nome da variável.
Ex1: Para armazenarmos o endereço num ponteiro: P := addr(x); ou P:=@x;
Ex2: Para exibirmos o endereço da variável x na tecla do micro, basta utilizar o comando
write
(LongInt(P)); Esse comando permite constatar que cada tipo de variável ocupa uma quantidade
diferente de memória.
Obs: Se usássemos simplesmente write (addr (x)), o Pascal daria um erro, pois ele não nos permite
escrever um endereço na tela diretamente. Para contornarmos esse problema, convertemos o
endereço para um tipo inteiro.
Questão 3.Qual a diferença entre P e P^ ?
R: O conteúdo de P é um endereço de memória, enquanto P^ é o conteúdo do endereço apontado
por P.
Questão 4. Um ponteiro aponta sempre para o endereço de uma variável?
R: Não! Podemos ter ponteiros que simplesmente apontam para uma área de memória sem nome
(ou seja, uma área de memória RAM em relação a qual nenhuma variável está associada).
Ex: O comando new(P) aloca um espaço de memória RAM e atribuiu o seu endereço à variável P.
Desse modo, a única maneira de acessarmos essa memória é pelo seu endereço, pois nenhuma
variável foi utilizada para nomear a memória.
Questão 5. Quando utilizamos o comando new(P), sabemos que uma área da memória é alocada e
seu endereço é atribuído a P. Entretanto, qual o tamanho e o formato da área alocada?
R: O tamanho e o formato da área alocada depende do tipo base do ponteiro. Abaixo são
mostrados vários exemplos:
Ex1: var
P1: ^char;
P2: ^real;
begin
new (P1); {cria uma área no formato do tipo char}
new (P2); {cria uma área no formato do tipo real}
P1^ := ‘A’;
Página 3
Ponteiros
Professor Sérgio Furgeri
P2^ := ’32.1’;
end.
Ex2:
type
Tfunc = record
nome: string;
idade: byte;
end;
var P: ^Tfunc;
begin
new (P); {alocar uma área de memória no formato Tfunc}
P^.nome:= ‘João Beterraba’;
P^.idade:= 20;
writeln (‘Eu sou o ‘, P^.nome,);
writeln (‘Tenho’, P^.idade, ‘ anos’);
end.
Conclusão: Para todos os efeitos, P^ pode ser entendida como sendo uma variável do mesmo tipo
que o tipo da base de P. Isso é sempre verdade, qualquer que seja o tipo base de P.
Questão 6. Como declaramos um ponteiro?
var
Sintaxe:
nome_ponteiro : ^tipo_base;
Ex:
var
P1 : ^char; {ponterio cujo tipo base é char}
P2 : ^real; {ponteiro cujo tipo base é real}
Questão 7. Por que devemos especificar um tipo base para um ponteiro?
R: É por meio do tipo base que o Pascal saberá quais são os tipos de valores que podem ser
armazenados nos endereços referenciados pelos ponteiros. Por exemplo, se o tipo base de um
ponteiro é o tipo char então somente valores do tipo char poderão ser armazenados no endereço
apontado pelo ponteiro.
Ex:
{ERRADO}
var
P : ^char;
begin
New (P);
P^ := 10; {o Pascal apontará um erro, pois estamos atribuindo um número a uma área de
end.
memória que somente pode armazenar char}
Página 4
Ponteiros
Professor Sérgio Furgeri
{CORRETO}
var
P : ^char;
begin
New (P);
P^ := ‘A’; {isso está correto, pois estamos armazenando o caracter ‘A’}
end.
Estruturas de dados com ponteiros
Exemplo 1: Um programa para verificar a memória disponível no sistema. (pág.105)
program MemDisp2;
pagina 165}
uses crt;
{modificado do livro turbo pascal 6 - completo e total,
var
p:^string;
begin
clrscr;
while true do
begin
writeln(memAvail);
new(p);
if memAvail<500 then
begin
Writeln('Nao ha memoria suficiente');
break;
end;
end;
readkey;
end.
Exemplo 2: Um programa que mostra o funcionamento do Dispose, seg() e Ofs().
program Dispose1; {retirado do livro turbo pascal 6 - completo e total, pagina
165}
uses crt;
var
p:^string;
begin
clrscr;
new(p);
p^:='1234567890';
writeln(p^);
writeln(seg(p)); { Segmento de mem¢ria }
writeln(ofs(p)); { Posicao de mem¢ria }
Writeln(MemAvail); { mostra a memoria disponivel }
dispose(p); { Libera a posicao de mem¢ria }
Writeln(MemAvail); { mostra a memoria disponivel }
writeln(p^);
{ coloca uma marca no conteudo de p }
Página 5
Ponteiros
Professor Sérgio Furgeri
readkey;
end.
Liberando espaço em memória
Tudo o que foi alocado
procedure dispose. Ex:
com
a
procedure
new,
deverá
ser
desalocado
com
var A: ^Integer;
begin
new(A);
{
Cria
uma
área
na
memória
para
alocar
um
Integer
A^:=10;
{
Coloca
o
valor
10
nessa
nova
área
da
memória
dispose(A);
{
Desaloca
o
espaço
reservado
pela
procedure
New
end.
a
}
}
}
Página 6
Download