A Pascalban alapban két lehetőségünk van a memória kezelésére. Statikusan és dinamikusan kezelhetjük a memóriát. A statikus memóriakezelést már valószínűleg mindenki ismeri, hiszen ezt használjuk akkor, amikor egy változót deklarálunk az eddig megismert módokon. A memóriakezelés e módját azért szokás statikusnak hívni, mert a változó deklarálása után automatikusan lefoglalódik a hely a memóriában és addig nem is szabadul fel, amíg a procedúrának vagy programnak vége nem szakad. Mellesleg ezzel a módszerrel egyszerre csak 64 Kbyte memóriához férünk hozzá, akármennyi is van a számítógépben.

A dinamikus memóriakezelés kicsit gázosabb de rengeteg előnye van, amiért érdemes mégis ezt is használni. Ha a dinamikus memóriakezelést választjuk, Real módban elérhetjük a 640 Kbyte alatti területet, Protected módban pedig csak a processzor címvonalainak száma szab határt a címezhető memória mennyiségének (jelenleg ez a határ 4Gb - gyakorlatilag a számítógép összes memóriájához hozzáférünk).

Mutató - Típusos és típus nélküli pointer
A mutató típusú változókról eddig is volt már szó, ez a Pointer. A mutató típusú változók abban különböznek a többi változótípustól, hogy ezek egy címet tartalmaznak és a címen van a számunkra fontos információ. Lássuk ezt ábrákkal! Az következő ábra a memóriának azt

a részét mutatja, amelyben a statikus változók vannak. Tegyük fel, hogy van egy Byte típusú változónk amit S-nek neveztünk el és az értéke 82. A memóriát a következő ábra mutatja.

Ha pointerrel mutatunk egy memóriaterületre, az a következő módon néz ki:

A pointer rámutat a memóriaterületre, ahol az információt tároljuk. Ha a pointer típus nélküli, akkor csak mutat valahová, de a mutatott címen lévő valamit nem tudjuk sok mindenre használni, hiszen nem tudjuk úgy megfogni, mint egy hagyományos változót. Ennél sokkal érdekesebbek a típusos pointerek, mert ezekkel a mutatott terület megfogható hasonló módon a statikus változókhoz.

Pointerek deklarálása
A típus nélküli pointerrel nincs sok gond:
Var   P: Pointer;

A típusos pointerek kicsit cifrábbak de ez sem vészes:

Type   Pbyte=^Byte;Var   P: Pbyte;

Ha a típusdeklarációban az új típus neve ^ jellel kezdődik, ez azt jelenti, hogy a ^ jel után szereplő típusú pointert hozzunk létre. A fenti példában az új típus a PByte ami Byte típusú pointer. Ebből a pointerből használtunk egyet a var deklarációban: A P változó PByte azaz Byte típusú pointer. (Ha olyan saját típust hozunk létre, amelyből kell az ilyen típusú pointer is, akkor a típus nevét "T", a típusos pointer nevét "P" betűvel szokás kezdeni. Pl: TMyRecord és PMyRecord)

Pointerek használata
A pointerek más jellegéből adódóan a használatuk is kicsit eltér a többi, már megismert változótípustól. Kezdetben a pointereknek is (mint mondjuk az Integernek) az értéke valami "hülyeség" hiszen a pascal nem szokta kinullázni az új változókat a deklarálás után. A pointereknél ez a hülye érték abban nyilvánul meg, hogy általában valami teljesen blőd helyre mutatnak.

A Pointereknél mindig ügyelni kell arra, hogy használatkor a mutató jó helyre mutasson! Különben lefagyástól hülye adatokon keresztül General Protection Fault-ig minden előfordulhat!

Pointerek kinullázása

A mutatókat nem lehet a hagyományos módon kinullázni (nem működik a P:=0) hiszen a pointer címet tartalmaz, következésképp csak címet lehet beleírni. Erre találták ki a NIL konstanst, mely a 0000:0000 címet tartalmazza. A mutatók kinullázása tehát: P:=NIL;

Figyelem! NIL-re mutató pointerre védett módban nem szabad hivatkozni!

A mutatott érték használata típusos pointernél

Kicsit bonyolultra sikerült a cím, de valójában egyszerű dolgokat takar. Arról van szó, hogy a pointereket többnyire azért használjuk, mert olyan memóriaterületeket akarunk elérni, melyeket statikus módszerrel nem tudnánk megcímezni. Az jó, hogy megvan a használni kívánt memóriaterület címe, de ettől még nem tudnánk használni a memóriaterületet, ha nem lenne a következő kis trükk: vegyünk egy P pointert, amely Byte típusú (ilyet deklaráltunk néhány bekezdéssel feljebb). Ha a programban a P szerepel, akkor pointerként viselkedik azaz a címet tudjuk változtatgatni. Ha a programban a P után elhelyezek egy ^ szimbólumot (P^), akkor a P^ úgy viselkedik mint egy Byte típusú változó azaz a mutató által mutatott értéket tudom változtatni.

Dinamikus memóriakezelés

A dinamikus memóriakezeléssel kapcsolatos utasítások a Pascal System unitjában vannak elhelyezve, így nem kell mindenféle unitokat használni a memória teljes kihasználásához.

A pascal rendelkezik egy HEAP memóriaterülettel, itt tudunk dinamikusan memóriát kezelni. A HEAP méretéről a bevezetőben ejtettem néhány szót. Egyébként a MEMAVAIL függvénnyel megnézhetjük a HEAP méretét. A függvény LongInt-et ad vissza.

Memóriafoglalás

A HEAP körülbelül úgy működik, mint egy hatalmas polcos szekrény, ahová könyveket rakhatunk fel vagy ahonnan könyveket vehetünk le. A polcok számozva vannak de mi nem rakosgathatunk mindenhová kedvünkre (esetleg kihányva más könyveket) mert azzal teljes káoszt idéznénk elő és azt ugye nem szeretnénk; ezért megkérjük a raktárost, a pascal memóriakezelőjét arra, hogy ugyan keressen már nekünk valahol 10 könyvnyi helyet a polcokon, mert mi könyveket akarunk berakosgatni. A memóriakezelő erre elrohan, majd visszatér annak a polcnak a számával, ahová elrakosgathatjuk a könyveinket. Hasonlóan működik ez a pascalban is:

  • GetMem(var P: Pointer; Size: Word);

  • A P a polc címe (a procedúra tölti ki), ahová pakolászhatunk, a Size pedig egy szám, ami azt adja meg, hogy hány bájtot kérünk (hány könyvnek akarunk helyet a polcokon). A P természetesen lehet típusos pointer is, de erre van egy másik memóriakezelő utasítás:
     
  • New(var P: Pointer [ , Init: Constructor ]);

  • Ezt az utasítást direkt típusos pointerekre találták ki, annyi bájtot foglal a memóriában amekkora a pointer típusának memóriafoglalása. Ha a pointer objektumra mutató pointer, akkor meg lehet adni az objektum Init konstruktorát amely a memóriafoglalás után azonnal végre fog hajtódni.
Memória felszabadítás

Visszatérve a könyvespolcra és a raktárosra, a memóriát célszerű felszabadítani, hiszen ha a könyvespolcokra folyton csak pakolászunk és soha sem mondjuk a raktárosnak, hogy kösz a könyvespolcot, már nem kell, ami rajta van, rakhatsz rá mást, előbb-utóbb megtelik az összes polc és imádott raktárosunk szívinfarktust kap. Az pedig nem jó…

  • FreeMem(var P: Pointer; Size: Word);

  • A pascal memóriakezelője a P pointer értékétől az Size számú bájtot felszabadítja. Vigyázzunk arra, hogy a foglaláskor és a felszabadításkor a két Size egyezzen, mert különben kisebb-nagyobb problémáink lesznek.
     
  • Dispose(var P: Pointer [ , Destructor ]);

  • Ezt szintén (hasonlóan a New-hoz) direkt típusos pointerekre találták ki, elvileg annyi bájtot szabadít fel, amennyi a pointer típusának kellett. Ha a pointer objektum típusú volt, akkor meg lehet adni a destruktort, ami közvetlenül az objektum memóriából való törlése előtt fog meghívódni.
Figyelem sosem szabad olyan pointerre hivatkozni, amelyet már diszpózoltunk vagy FreeMem-eltünk!
Type   Tomb1=Array[1..10000] of Byte;   PTomb1=^Tomb1; Var   P: Ptomb1; Begin   P^[1]:=99;   GetMem(P, SizeOf(Tomb1));   P^[1]:=100;   FreeMem(P, SizeOf(Tomb1));   P^[1]:=101;End.
A fenti program egyszerű példa a dinamikus memóriakezelésre. A piros sorok mutatják, hogy mit nem szabad csinálni. Az első piros sorban még nem nyúlhatunk a P pointerhez hiszen hülyeségeket mutat (ha egy ilyen sor benne marad egy programban akkor több mint valószínű, hogy sikeresen le fogja fagyasztani a számítógépet). A második piros sor azért helytelen, mert a P már olyan helyre mutat, ahol hivatalosan semmi sincs (A freemem-mel az előbb szabadítottuk fel azt a memóriaterületet, ahová a P mutatott).

Ennyi szenvedés után jogos a kérdés, miért ez a sok cécó, amikor a statikus memóriakezeléssel is jól elvan az ember? A válasz viszonylag egyszerű: A hatalmas elérhető terület miatt érdemes dinamikusan memóriát kezelni, valamint lehet olyan dolgokat csinálni, amelyek nagyon körülményesek lennének statikus módszerekkel (Ha a pascal nem támogatná a dinamikus memóriakezelést, akkor a Tvision sem létezne, mert a Tvision igen nagy részben épül a dinamikus memóriakezelés lehetőségeire).

Hibakezelés

Az egyetlen lekezelhető hiba a HEAP megtelése. Az előbb azt állítottam, hogy ha a raktáros nem talál megfelelő számú üres polcot akkor szívinfarktus kap. Azért ez így, ebben a formában nem teljesen igaz, hiszen ma már léteznek megfelelő szívgyógyszerek:

A pascal memóriakezelőjének van egy funkció típusú változója a HEAPERROR ami akkor hívódik meg, ha a New vagy GetMem utasítások által kért memória nem áll rendelkezésre. A HeapError funkció helyére berakhatjuk a saját hibakezelő eljárásunkat:

Program HeapHibakezeles; Function MyHeapError(Size: Word): Integer; far;Begin   WriteLN('Le akartál fogalni ',Size,' byte memóriát');   WriteLN('de pechedre csak ',memavail,' byte szabad hely volt a HEAP-ben');   WriteLN('Kész0lj a Halálra!');   MyHeapError:=0;End;Begin   HeapError:=@MyHeapError;End.
A fenti programban nem túl értelmes a hibakezelés, hiszen a program csak kiírja a fent látható mélyértelmű szöveget majd megáll a normális pascalos hibaüzenettel: Runtime Error 203 at XXXX:XXXX. Ha csak ennyit tudnánk tenni a hibakezeléskor, akkor semmit sem érne az egész. Lehetőségünk van azonban ennél picit intelligensebb megoldásokra is. A HeapError nem véletlenül Function. A HeapError visszatérési értéke dönti el, hogy mi történjen miután lefutott a hibakezelő programrész.

Ha a visszatérési érték 0 akkor a program megáll a szokásos hibaüzenettel.

Ha a visszatérési érték 1 akkor a program nem áll meg, a pascal memóriakezelője a New vagy GetMem eredményeként a polc címének NIL-t ad vissza.

Figyelem!! Ha ezt a hibakezelést használjuk, mindig ellenőrizni kell a New és a GetMem után, hogy a kapott memóriacím nem NIL-e. Ha ezt elmulasztjuk és egy NIL-re mutató pointerre hivatkozunk szinte biztos, hogy lefagy a számítógép.

Ha a visszatérési érték 2 akkor a memóriakezelő újra próbálkozik a memóriafoglalással. Ha most sem sikerül, akkor újra meghívódik a HeapError.

Első körben talán ennyit a dinamikus memóriakezelésről. Másik rovatainkban ez folyton előjövő téma, így ha bővebben érdekel benneteket a téma, akkor érdemes belelapozni a Delphibe is.

Ez volt a Pascal Iskola utolsó száma, de ha van kérdésetek, írjátok!