dyn.pdf

(341 KB) Pobierz
Zmienne dynamiczne
Informatyka EP AGH Kr
- 1 -
Zmienne dynamiczne - podstawowe pojęcia
Każdy skompilowany program, napisany w Turbo/Borland Pascalu zajmuje cześć pamięci operacyjnej komputera , w sposób
przedstawiony na rysunku poniżej.
HeapEnd
wolny obszar stosu
HeapPtr
stos dla zmiennych dynamicznych
bufor nakładkowy
HeapOrg
OvrHeapEnd
OvrHeapOrg
segment stosowy dla zmiennych lokalnych
SSeg:SPtr
wolny obszar segmentu stosowego
SSeg:0000
zmienne globalne
segment danych
literały zmienne
DSeg:0000
blok kodu modułu System
blok kodu pierwszego modułu
zawartość obrazu
bloki kodu kolejnych modułów
zbioru .EXE
blok kodu ostatniego modułu
blok kodu programu
blok wstępny programu (PSP)
PrefixSeg
Blok wstępny Þ Kod programu Þ Kody modułów Þ
4 Podczas wczytywania zbioru .EXE do pamięci, system operacyjny DOS tworzy blok wstępny programu (blok PSP ). Ten obszar
pamięci służy do komunikacji pomiędzy systemem operacyjnym i programem. Blok PSP (Program Segment Prefix) zajmuje 256 bajtów.
Adres segmentu pamięci, od którego rozpoczyna się ten blok jest pamiętany w predefiniowanej zmiennej PrefixSeg.
4 Kod programu głównego zajmuje pierwszy blok kodowy .
4 W kolejnych blokach pamiętane są kody zadeklarowanych w programie modułów (w odwrotnej kolejności niż zadeklarowane). W
ostatnim bloku kodowym pamiętany jest kod standardowego modułu System .
4 Żaden z bloków kodowych nie może zając więcej niż 64 kB pamięci, natomiast rozmiar wszystkich bloków łącznie jest
ograniczony jedynie wielkością pamięci komputera.
Segment danych Þ Segment stosowy Þ Bufor nakładkowy Þ
W segmencie danych pamiętane są wszystkie literały zmienne zdefiniowane w programie i modułach oraz zmienne globalne
zadeklarowane w programie i modułach. Początek adresu tego segmentu jest wartością standardowej funkcji DSeg. Rozmiar segmentu
danych (podobnie jak poszczególnych segmentów kodowych) nie może przekraczać 64 kB.
W segmencie stosowym pamiętane są zmienne lokalne. Kolejne zmienne zapamiętywane są w kierunku malejących adresów
pamięci. Rozmiar segmentu stosowego (max 64 kB) ustala się za pomocą dyrektywy kompilatora M lub w menu systemu Turbo/Borland
Pascal.
Do przechowywania segmentów nakładkowych programu jest wykorzystywany bufor nakładkowy. Jeśli program nie posiada
segmentów nakładkowych, rozmiar tego bufora jest równy zeru. Adresy początku i końca bufora nakładkowego pamiętane są
w predefiniowanych zmiennych OvrHeapOrg i OvrHeapEnd.
Zmienne statyczne a dynamiczne Þ Sterta Þ
Zmienne typu statycznego (globalne - umieszczane w segmencie danych i lokalne - umieszczane w segmencie stosowym) istnieją
przez cały czas wykonywania tej części programu, w której są zadeklarowane . Każdy z tych segmentów ma rozmiar
maksymalny równy 65635 bajtów.
4 W Turbo/Borland Pascalu, obok zmiennych statycznych występują zmienne dynamiczne reprezentujące obiekty dla
których pamięć jest przydzielana na określone żądanie. Zmienne te nie posiadają identyfikatorów, a odwołanie do nich
następuje za pomocą wskaźnika. Wartościami zmiennych dynamicznych są elementy typu wskaźnikowego, które
określają adresy pamięci zmiennych dynamicznych.
Zmienne dynamiczne przechowywane są na stosie dla zmiennych dynamicznych (na stercie) . Pamięć dla tych zmiennych jest
przydzielana za pomocą standardowych procedur New i GetMem. Zmienne dynamiczne mogą zajmować całą resztę pamięci dostępną
podczas wykonywania programu. Adres początku stosu pamiętany jest w zmiennej HeapOrg, a adres wierzchołka stosu w zmiennej
HeapPtr.
727205694.001.png
Informatyka EP AGH Kr
- 2 -
Definicja typu wskaźnikowego Þ
Definicja typu wskaźnikowego ma postać:
type identyfikator_typu_wskaźnikowego = ^identyfikator_typu_bazowego;
Zmienne typu wskaźnikowego wskazują na dane typu bazowego.
Identyfikator typu bazowego może być określony wcześniej lub w tym samym
zdaniu type, w którym występuje definicja danego typu wskaźnikowego.
Przykłady:
a)
type Macierz = Array[1..10, 1..10] of Byte;
Macierz_dynamiczna = ^Macierz;
b)
type Lista_dynamiczna = ^Lista;
Lista = record
Lp : Byte
X,Y : Real;
end;
c)
type Liczba = ^LongInt;
Zmienne wskaźnikowe Þ
Po wprowadzeniu deklaracji:
var M : Macierz_dynamiczna;
L : Lista_dynamiczna;
zmiennej wskaźnikowej M można będzie w programie przypisywać adresy pamięci danych (zmiennych dynamicznych) typu Macierz,
natomiast zmiennej wskaźnikowej L - adresy pamięci danych (zmiennych dynamicznych) typu Lista.
Deklaracja zmiennej wskaźnikowej
var x : Liczba
umożliwi przypisanie zmiennej x adresów pamięci danych (zmiennych dynamicznych) typu LongInt.
Zmienne wskaźnikowe można również zadeklarować następująco:
var M1 : ^Macierz; L1 : ^Lista; X1 : ^LongInt;
Zmienne dynamiczne pamiętane są segmencie pamięci o strukturze stosowej.
Zmienne wskazywane Þ
Aby odwołać się do zmiennej typu wskaźnikowego stosuje się zmienne wskazywane postaci:
zmienna_wskaźnikowa^
Przykład:
type Lista = record
Lp : Byte; X,Y : Real;
end;
Lista_dynamiczna = ^Lista;
var
L : Lista_dynamiczna;
Zmienna wskaźnikowa L jest związana z typem wskaźnikowym Lista_dynamiczna. Wartościami zmiennej wskaźnikowej L będą adresy
pamięci danych (zmiennych dynamicznych) typu Lista. Zmienna wskazywana L^ będzie natomiast rekordem typu Lista o adresie
określonym przez zmienną wskaźnikową L.
Odwołania za pomocą zmiennych wskazywanych: Þ
Odwołania typu
L.Lp:=1;
L.X:=10.0;
L.Y:=20.0;
są błędne, ponieważ L jest zmienną wskaźnikową.
Poprawne odwołanie powinno być zapisane za pomocą zmiennych wskazywanych:
L^.Lp:=1; L^.X:=10.0;
(wcześniej jednak należy w odpowiedni sposób utworzyć zmienną dynamiczną).
Zmienne wskazywane stosuje się przede wszystkim do dynamicznego zarządzania pamięcią operacyjną. Do tworzenia i zwalniania
zmiennych dynamicznych oraz do zarządzania obszarem pamięci przeznaczonym na zmienne dynamiczne służą procedury i funkcje
standardowe:
4 New, GetMem - do utworzenia zmiennej dynamicznej i podstawienia pod zmienną wskaźnikową
odpowiedniego adresu pamięci.
4 Dispose, FreeMem, Release - do zwolnienia obszaru pamięci dynamicznej (usunięcia odpowiedniej
zmiennej dynamicznej z pamięci).
4 Mark, MaxAvail, MemAvail.
L^.Y:=20.0;
Informatyka EP AGH Kr
- 3 -
4 Procedura New
New (zmienna_wskaźnikowa)
lub
New (zmienna_wskaźnikowa, konstruktor) - do utworzenia zmiennej dynamicznej typu obiektowego .
Przykład:
New(L);
Do utworzonej zmiennej dynamicznej można odwołać się za pomocą zmiennej wskazywanej postaci zmienna_wskaźnikowa^, np:
L^.Lp:=1; L^.X:=10.0; L^.Y:=20.0;
Zmiennej dynamicznej tworzonej za pomocą procedury New jest przydzielany blok pamięci równy rozmiarowi typu, z którym związana
jest wyspecyfikowana zmienna wskaźnikowa. W przykładzie powyżej, zmiennej dynamicznej będzie przydzielony blok pamięci równy 13
bajtów (Lp - 1 bajt, X,Y - po 6 bajtów).
4 Procedura GetMem
GetMem (zmienna_wskaźnikowa, rozmiar_pamięci)
Rozmiar_pamieci jest wyrażeniem typu Word określającym rozmiar pamięci rezerwowany na zmienną dynamiczną.
Przykład:
GetMem(L,13);
jest równoważne
New(L);
4 Procedura Dispose
Dispose (zmienna_wskaźnikowa)
lub Dispose (zmienna_wskaźnikowa, destruktor) - do zwolnienia pamięci zajmowanej przez obiekt dynamiczny.
Procedura Dispose zwalnia obszar pamięci zajmowany przez zmienną wskazywaną.
Na przykład:
Dispose (L);
(Uwaga, wymagane jest, aby zwalniana zmienna dynamiczna była uprzednio utworzona za pomocą procedury New).
4 Procedura FreeMem
FreeMem (zmienna_wskaźnikowa, rozmiar_pamięci)
Procedura ta zwraca do stosu przeznaczonego na zmienne dynamiczne obszar pamięci wskazywany zmienną_wskaźnikową i zajmujący
liczbę bajtów określoną argumentem rozmiar_pamięci, Np.
FreeMem (L,13);
(Uwaga, wymagane jest, aby rozmiar_pamięci był taki sam jak w wywołaniu procedury GetMem tworzącej tę zmienną dynamiczną.
4 Procedura Release
Release (zmienna_wskaźnikowa)
Procedura ta usuwa ze stosu zmienną dynamiczną wskazywaną zmienną_wskaźnikową oraz wszystkie zmienne następujące po niej.
Przykład:
{ $M 30000,0,655008}
type Wektor_statyczny = Array [1..50] of Byte ;
Wektor_dynamiczny = ^Wektor_statyczny;
Macierz_dynamiczna = Array [1..100] of Wektor_dynamiczny;
var
M : Macierz_dynamiczna; i, j : Integer ; rozmiar: LongInt ;
begin
rozmiar:= SizeOf (M); { wyznaczenie rozmiaru zmiennej wskaźnikowej M }
Writeln ('Maksymalny blok pamieci : ', MaxAvail );
Writeln ('Rozmiar zmiennej wskaznikowej M : ',rozmiar);
for i:=1 to 100 do New (M[i]);
{ przydzielenie pamięci zmiennej dynamicznej wskazywanej przez M[i] }
for i:=1 to 100 do
for j:=1 to 50 do M[i]^[j]:=i+j;
{ wartość i+j jest przypisywana zmiennej wskazywanej o adresie określonym przez M[i] }
for i:=1 to 100 do
for j:=1 to 50 do Writeln (M[i]^[j]);
for i:=100 downto 1 do Dispose (M[i]);
{ zwolnienie obszaru pamięci zajmowanego przez zmienną dynamiczną wskazywaną przez M[i]}
end .
Informatyka EP AGH Kr
- 4 -
Przykład programu umożliwiającego analizę wykorzystania pamięci operacyjnej
program analiza;
{$M 1024,0,30}
type s60
= String [60];
var
a,b,c,d,s : LongInt ;
g,h,i
: ^Double ;
t
: Text ;
procedure pisz(napis:S60; liczba:LongInt);
begin
Writeln(t,napis:60,liczba:30)
end ;
procedure pisz10 (napis:S60; liczba1,liczba2:LongInt);
begin
Write(t,napis:60,' ',liczba1,':');
case liczba2 of
0..9 : Write (t, '000',liczba2);
10..99 : Write (t, '00',liczba2);
99..999 : Write (t, '0',liczba2)
else Write (t, liczba2);
end ; {case }
Write (t, (liczba1*16+liczba2):16 );
end ; { pisz10 }
procedure pisz16(liczba1,liczba2:Word);
const znak : Array [0..$F] of Char = '0123456789ABCDEF';
begin
Write(t,' ');
Write(t, znak [ Hi (liczba1) shr 4] );
Write(t, znak [ Hi (liczba1) and $F] );
Write(t, znak [ Lo (liczba1) shr 4] );
Write(t, znak [ Lo (liczba1) and $F] );
Write(t,':');
Write(t, znak [ Hi (liczba2) shr 4] );
Write(t, znak [ Hi (liczba2) and $F] );
Write(t, znak [ Lo (liczba2) shr 4] );
Write(t, znak [ Lo (liczba2) and $F] );
Writeln(t);
end ; { pisz16 }
procedure jeden;
var d,e,f: Double ;
begin
pisz10('Adres wolnego segmentu stos. dla zm.lokalnych', SSeg ,0);
pisz16( SSeg ,0);
Writeln(t);
pisz('Wolna przestrzen segmentu dla zm. lokalnych *', SPtr );
pisz10('Adres ost. zajetego bajtu w seg. stos. *', SSeg , SPtr );
pisz16( SSeg , SPtr );
Informatyka EP AGH Kr
- 5 -
pisz10('Adres 1-ego zajetego bajtu w seg. stos. *', SSeg ,1023);
pisz16( SSeg ,1023);
pisz('Rozmiar segmentu stosowego zajetego przez zmienne lokalne *',(s* OvrHeapOrg -(s* SSeg + SPtr )) );
Writeln(t);
pisz('Razem max. rozmiar segmentu stosowego dla zmiennych lokalnych',s*( OvrHeapOrg - SSeg ) );
Writeln(t);
pisz10('Adres zmiennej lokalnej d {rozmiar 8}', Seg (d), Ofs (d) );
pisz16( Seg (d), Ofs (d));
pisz10('Adres zmiennej lokalnej e {rozmiar 8}', Seg (e), Ofs (e) );
pisz16( Seg (e), Ofs (e));
pisz10('Adres zmiennej lokalnej f {rozmiar8 }', Seg (f), Ofs (f) );
pisz16( Seg (f), Ofs (f));
Writeln(t);
end ; { jeden }
procedure dwa;
begin
pisz10('Adres zmiennej dynamicznej wskazywanej przez g^ {rozmiar 8}', Seg (g^), Ofs (g^));
pisz16( Seg (g^), Ofs (g^));
pisz10('Adres zmiennej dynamicznej wskazywanej przez h^ {rozmiar 8}', Seg (h^), Ofs (h^));
pisz16( Seg (h^), Ofs (h^));
pisz10('Adres zmiennej dynamicznej wskazywanej przez i^ {rozmiar 8}', Seg (i^), Ofs (i^));
pisz16( Seg (i^), Ofs (i^));
end ; { dwa }
begin { analiza }
Assign (t,'8.res');
Rewrite (t);
s:=16;
for a:=1 to 66 do Write(t,' ');
Writeln(t,'Seg:Ofs Seg*16+Ofs $$$$:FFFF');
Writeln(t);
pisz10('Adres poczatku bloku PSP', PrefixSeg ,0);
pisz16( PrefixSeg ,0);
pisz('Rozmiar bloku PSP',256);
Writeln(t);
pisz10('Adres bloku kodu programu od', CSeg ,0);
pisz16( CSeg ,0);
for a:=1 to 42 do Write(t,' ');
Writeln(t,'Rozmiar zbioru EXE',s*( DSeg - CSeg ):30);
Writeln(t);
pisz10('Adres poczatku segmentu danych', DSeg ,0);
pisz16( DSeg ,0);
pisz('Rozmiar segmentu danych',s*( SSeg - DSeg ));
Writeln(t);
pisz10('Adres zmiennej globalnej a {4 bajty }', Seg (a), Ofs (a));
pisz16( Seg (a), Ofs (a));
pisz10('Adres zmiennej globalnej b {4 bajty }', Seg (b), Ofs (b));
Zgłoś jeśli naruszono regulamin