[ Pobierz całość w formacie PDF ]
tworzenia i niszczenia w zależności od potrzeb. Wymaga to co prawda użycia spec-
jalnych procedur, pozwala jednak na znacznie bardziej efektywną gospodarkę pamięcią.
W odróżnieniu od statycznych zmiennych globalnych, istniejących przez cały czas
wykonywania programu, zmienne wskazywane należą do klasy zmiennych dynamicz-
nych (czyli istnieją dokładnie wtedy, gdy życzy sobie tego programista). Sam proces
utworzenia zmiennej dynamicznej polega na zarezerwowaniu odpowiedniego obszaru
pamięci i zapamiętaniu adresu tego obszaru we wskazniku wskazującym na zmienną.
Z kolei usunięcie zmiennej powoduje zwolnienie rezerwacji (zawartości zmiennej
oraz wskaznika nie są fizycznie niszczone, ale lepiej się już do nich nie odwoływać).
Ceną za możliwość swobodnego przydzielania i zwalniania pamięci jest konieczność
bezwzględnego inicjalizowania wskazników, które przed utworzeniem wskazywanej
zmiennej mają wartość nieokreśloną (zwykle zero, co odpowiada wskaznikowi nil, nie
wskazującemu na żaden obiekt). Próba odczytu zmiennej wskazywanej przez taki wska-
znik dostarczy tylko bezsensownego wyniku, natomiast próba zapisu (w
przypadkowe miejsce pamięci!!) może skończyć się zawieszeniem komputera lub
zupełnie nieprzewidzianym jego zachowaniem.
Pamiętaj o inicjalizacji wskazników!
Więcej pamięci! 117
Turbo Pascal oferuje kilka metod tworzenia i usuwania zmiennych dynamicznych,
z których najpopularniejszą realizuje para procedur new i dispose:
-do-zmiennej)
-do-zmiennej)
Procedura new wykonuje czynności związane z utworzeniem zmiennej wskazywanej,
natomiast dispose operacje związane z jej usunięciem. Drugą parę zarządzającą
dynamicznym przydziałem pamięci tworzą procedury GetMem i FreeMem:
-bloku)
-bloku)
W odróżnieniu od pary new-dispose, procedury te wykorzystują wskazniki
amorficzne (typu pointer) i służą do bardziej wewnętrznego manipulowania
pamięcią, tj. przydzielania i zwalniania bloków bajtów (a nie zmiennych wskazywanych
jakiegoś konkretnego typu). Wielkość przydzielanego lub zwalnianego bloku (w
bajtach) określa parametr rozmiar-bloku. Korzystając z obu grup procedur musisz
pamiętać, że pamięć przydzielona przez GetMem nie może być zwolniona procedurą
dispose i odwrotnie.
Ostatnią, chyba najrzadziej stosowaną parę tworzą procedury mark i release:
Wykonanie procedury mark nie powoduje przydzielenia pamięci ani utworzenia zmien-
nej, a jedynie zapamiętanie bieżącej wysokości sterty w zmiennej . Zwol-
nienia całego obszaru sterty leżącego powyżej dokonuje się za pomocą
procedury release. Obydwie procedury stosowane są podobnie jak GetMem
i FreeMem głównie w programowaniu niskiego poziomu, do masowego
zwalniania pamięci przydzielonej na stercie.
Korzystając ze zmiennych dynamicznych musisz pamiętać, że sterta nie jest automa-
tycznie porządkowana, toteż kolejne operacje przydzielenia i zwolnienia bloków pamię-
ci (dowolną metodą) mogą doprowadzić do tzw. fragmentacji, czyli rozbicia wolnego
jeszcze obszaru pamięci na mniejsze, rozłączne bloki. Ponieważ rozmiar tworzonej
zmiennej dynamicznej nie może być większy od rozmiaru największego wolnego bloku
pamięci, może się okazać, że próba utworzenia zmiennej skończy się niepowodzeniem,
mimo iż wielkość dostępnej pamięci będzie wystarczająca. Dlatego też właściwą miarą
możliwości utworzenia większej struktury danych na stercie jest nie funkcja MemAvail
(zwracająca sumaryczny rozmiar wolnej pamięci), lecz MaxAvail (zwracająca rozmiar
największego wolnego bloku).
Pora na przykłady. Na początek przedstawimy zbiorczą demonstrację możliwości ob-
sługi zmiennych dynamicznych.
program ZmienneDynamiczne;
type
118 Turbo Pascal programowanie
TabReal = array[1..5000] of real; { tablica liczb }
{ rzeczywistych }
PString = ^string
var
TabTabReal : array[1..100] of ^TabReal; { tablica }
ty }
i : integer; { pomocniczy licznik }
procedure IlePamieci;
begin
writeln('Wolne: ', MemAvail, ' max. blok: ', MaxAvail,
' bajtow.');
end;
begin
writeln(s^); { zmienna nie utworzona }
writeln(s^); { utworzona, lecz nie zainicjalizowana }
s^ := 'No wreszcie!'; { inicjalizujemy }
writeln(s^); { teraz jest OK }
dispose(s); { usuwamy }
mark(Sterta); { zaznaczamy 'poziom' sterty }
while MemAvail > SizeOf(TabReal) do { tyle wierszy )
begin
new(TabTabReal[i]); { tworzymy nowy wiersz }
end;
dispose(TabTabReal[3]); { usuwamy jeden wiersz tablicy }
IlePamieci;
IlePamieci;
end.
Pierwsza część programu demonstruje etapy tworzenia, wykorzystania i usunięcia
zmiennej wskazywanej (w naszym przypadku łańcucha) za pomocą procedur new
i dispose. Zauważ, że utworzenie zmiennej wskazywanej nie jest równoznaczne z jej
inicjalizacją, a po wykonaniu procedury dispose treść łańcucha nie jest niszczona,
chociaż może być niekompletna.
Druga część tworzy typową strukturę wielkiej tablicy, przydzielając pamięć dla
poszczególnych wierszy, dopóki to jest możliwe. Zauważ, że po usunięciu trzeciego
wiersza tablicy na ogół okazuje się, że rozmiar największego dostępnego bloku jest
mniejszy od całkowitego rozmiaru wolnego obszaru sterty, co uniemożliwia tworzenie
[ Pobierz całość w formacie PDF ]