[ Pobierz całość w formacie PDF ]
tycznie w dowolnym miejscu pamięci (dokładnie, zmienne wskazywane umieszczane są
na tzw. stercie (ang. heap) wydzielonym obszarze pamięci zarządzanym przez
specjalne procedury).
Deklaracja wskaznika do zmiennej określonego typu jest bardzo podobna do zwykłej
deklaracji zmiennej i różni się od niej jedynie symbolem wskaznika (^):
zmienna : ^typ
przy czym typ musi być identyfikatorem typu prostego lub zdefiniowanego wcześniej
w sekcji type. Odwołanie do samego wskaznika wygląda dokładnie tak samo, jak dla
każdej innej zmiennej, natomiast odwołanie do wskazywanej przez niego zmiennej
zawiera dodatkowo znaczek ^, tym razem położony za nazwą wskaznika:
zmienna^
A oto kilka przykładów:
type
TabReal = array[1..200] of real; { tablica 100 liczb real }
string100 = string
var
TabTabReal : array[1..200] of
116 Turbo Pascal programowanie
{ ... }
TabTabReal[10]^[30] := 12.345;
Pstring100^ := 'Lancuch dynamiczny';
writeln(PString100^);
Na szczególną uwagę zasługuje tu zmienna TabTabReal, będąca tablicą wskazników
do tablic liczb rzeczywistych. Struktura tego typu pozwala na efektywne przechowanie
dwuwymiarowej tablicy składającej się z 200 wierszy (wskazywanych wskaznikami)
zawierających po 200 liczb każdy, czyli w sumie 40 tysięcy liczb o objętości około 240
kB! Umieszczoną nieco niżej instrukcję TabTabReal[10]^[30] := 12.345 można
wytłumaczyć jako odwołanie do trzydziestej komórki w dziesiątym wierszu, czyli
tablicy wskazywanej przez dziesiąty wskaznik w tablicy TabTabReal. Jak nietrudno
się już domyślić, głównym zastosowaniem wskazników jest obsługa dużych struktur
danych (np. tablic rekordów, czyli katalogów bibliotecznych...). Z tego też względu
mały sens ma deklaracja zmiennej pi, gdyż liczba typu integer zajmuje dwa bajty,
zaś wskaznik cztery.
Warto tu jeszcze wspomnieć, że oprócz wskazników do obiektów konkretnego typu
Turbo Pascal udostępnia również wskazniki amorficzne, tj. wskazujące na obszar pa-
mięci i nie związane z żadnym konkretnym typem. Wskaznik taki deklarowany jest
słowem pointer (wskaznik) i jest zgodny pod względem przypisania z innymi typami
wskaznikowymi. Typ pointer i związane z nim operacje wykorzystywane są przez
bardziej zaawansowanych programistów do bezpośredniego, niskopoziomowego
manipulowania zawartością pamięci.
Drugą bardzo ważną i pożyteczną cechą zmiennych wskazywanych jest możliwość ich
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 }
[ Pobierz całość w formacie PDF ]