Matkönyv megjelenítő főoldalProgramraktár főoldal

A Pascal programokban szereplő adattípusokról és szerkezetekről

Ez a dokumentáció azokról az adattípusokról és programozási szerkezetekről szól, amelyek a programraktárban lévő Pascal programokban előfordulnak, de az iskolai Pascal-ismeretekkel nem biztos, hogy érthetőek. Azonban a Pascalt jobban ismerőknek, és főleg a programraktár bővítőinek sem árt átfutni, hogy lássák a programraktár deklarációinak és adattípusainak a koncepcióját.

Eljárások, függvények

Az eljárás előre elkészített utasítássorozat, amelyet deklarálása után a programból meghívhatunk, azaz végrehajthatunk. A program deklarációs részében kell definiálni, ott, ahol a típusokat, konstansokat, változókat stb. A definíció a következő részekből áll:

procedure eljárásnév(formális paraméterlista);
  deklarációk, mint a főprogramban
begin
  utasítások, mint a főprogramban
end;

Az eljárásban ugyanúgy deklarálhatunk változókat (ún. lokális változókat), címkéket, stb. mint a főprogramban (bár típusokat és konstansokat ritkán szoktunk), de ezeket csak az eljáráson belül érhetjük el. Ha az eljáráson belül és a főprogramban ugyanolyan nevű azonosító van, az eljáráson belülit tudjuk elérni. Az eljárás deklarációs részében akár másik eljárást is definiálhatunk, ilyenkor ezt is csak az eljárásból tudjuk hívni. (Ilyet akkor célszerű írni, ha meg akarjuk előzni, hogy a csak a külső eljárásban használt eljárás neve ütközzön egy tőle függetlenül deklarált azonosítóval, és a használt eljárást úgyis csak "belső használatra" szánják.) Egy eljárásban deklarált azonosító az eljárásból hívott másik eljárásban nem érhető el, kivéve, ha azt ennek az eljárásnak a deklarációs részében, a kérdéses azonosító után deklarálták.

Az eljárásnak át lehet adni adatokat, amelyek a működését meghatározzák. Hogy egy eljárás milyen paramétereket vár, meg kell adni zárójelek között, a formális paraméterlistában. Ezek szintén lokális változók, amiknek a kezdeti értékét a hívó program állítja be. A paramétereket pontosvesszővel kell egymástól elválasztani, minden paraméternek meg kell adni a nevét, és : után a típusát. A : előtt vesszővel elválasztva több nevet, is megadhatunk, ez több azonos típusú paramétert jelent. Pl. a
procedure example_proc(S:string; i1,i2:integer);
fejlécű eljárásnak egy string és két integer típusú paramétere van. Ha nincs paraméter, a zárójelek elhagyhatók.

Egy eljárást úgy hívhatunk meg, hogy megadjuk a nevét, majd, ha vannak, zárójelek között, vesszővel elválasztva a paramétereit. Pl. az előbbi eljárás lehetséges hívása:
example_proc('Hello, world',1,3);

Az eddig bemutatott paraméterátadási mód, az érték szerinti paraméterátadás esetén a paraméter minden szempontból lokális változó lesz, így ha az eljáráson belül megváltoztatjuk, az a hívó programra semmilyen közvetlen hatással nincs. Létezik viszont egy olyan paraméterátadási mód, ahol paraméternek kötelezően változót kell megadni, és a formális paraméterlistában megadott név csak a külső változó "álneve" lesz, annak eljáráson belüli módosításai a hívó környezetben is megváltoztatják a változó értékét. Azt, hogy egy eljárás ilyen "cím szerinti paramétert" vár, a formális paraméterlistában a paraméter neve előtti var kulcsszóval lehet jelezni. Ez a legközelebbi pontosvesszőig hat ki, tehát egy paraméterre, vagy vesszővel elválasztott paraméterek csoportjára.Pl. a
procedure example_proc(var S:string; i1,i2:integer);
fejlécű eljárásnak az első paramétere lesz cím szerint átadott.

Szükség lehet arra, hogy egy ilyen utasítássorozat kiszámítson valamilyen adatot, és visszaadja azt a főprogramnak. Ilyenkor eljárás helyett függvényt kell írni (procedure helyett function kulcsszó), és megadni a visszaadott érték típusát. A függvény fejléce:
function függvénynév(formális paraméterlista):visszatérési típus;
A paraméterlista és a függvény többi része az eljárással azonos felépítésű.

A függvény hívásakor szintén a nevét és a paramétereit kell megadni, de nem önmagában, hanem egy kifejezésben lehet használni, pl. a visszaadott értéket elraktározhatjuk egy változóban:
v:=example_func(5);
, ha a függvénynek egytlen, szám típusú paramétere van.

Ha egy szubrutinból több adatot kell visszaadni, arra több lehetséges megoldás van:

A programraktárban változó szerinti paramétereket, ill. tömbök esetén mutatókat, dinamikus tömböket használunk.

Típusok

Rekordok

Rekordokra bonyulultabb adattárolási szerkezeteknél (pl. láncolt listáknál) van szükség. Ezek olyan típusok, amelyekkel egyetlen változóban (vagy tömbelemben) tárolhatunk meghatározott számú, különböző típusú adatot ("mezőt"). Egy rekord típus deklarációja (amit a : után, de inkább a type deklarációban kell írni):
record
  mezőnév1,mezőnév2,...:típus;
  mezőnév1,mezőnév2,...:típus2;
  ...
end;

Mutatók

A számítógép memóriájában minden bájtnak van egy (általában 32 bites) címe. A mutató típusú változóban ilyen címet tárolhatunk.

Az egész memóriának azonban mindig csak egy része van lefoglalva a programunk számára, a többi része szabad, vagy más programok használják. A program a következő módokon használhat memóriát:

Egy mutató típusú változó tartalmazhat:

A mutató deklarációkor meg kell adni, hogy milyen típusú adatra mutathat (ez röviden a mutató típusa). Adott típusú mutató deklarálásakor így kell a típust megadni: ^típus.

Ha egy mutató (mondjuk, p) érvényes memóriacímet tartalmaz, a mutatott adatot így kérdezhetjük le: p^.

Egy mutatóba a következő módokon kerülhet érvényes memóriacím:

Mutatók használata dinamikus tömbök létrehozásához

Sokszor előfordul olyan eset, hogy egy algoritmus adatok listáján dolgozik. Hogy mennyi adaton, az csak az adott szubrutint használó programban dől el, illetve sokszor csak a program futása közben, hiszen egy jó programnál a felhasználó mondja meg, hogy mennyi adaton dolgozzon. Ezért nem használunk statikus tömböket, ahol a tömb méretét előre meg kell adni. A Free Pascal rendelkezik beépített dinamikus tömb támogatással, de a Turbo Pascal nem, ezért a Turbo Pascal programokban mutatókat használunk dinamikus tömbök létrehozására. Amikor a program megtudja, mennyi adatot fog tartalmazni a tömb, egy mutatóba a getmem paranccsal lefoglal ennek megfelelő mennyiségű memóriát, és mikor már nincs szükség az adatokra, felszabadítja. Az elemeket egy "trükkel" érhetjük el: a mutatót egésszé konvertáljuk, mégpedig longint-té, mert DOS-ban annak a mérete egyezik meg a mutatóéval, hozzáadunk annyit, hogy, mint mutató, a kívánt sorszámú elemre mutasson, és visszakonvertáljuk mutatóvá. Típusok között így konvertálhatunk: céltípus(konvertálandó érték). Mutatóvá való konverzióhoz azonban deklarálni kell a mutató típusát, ezért tegyük fel, hogy a pword típust deklaráltuk, mint ^word (kétbájtos, előjel nélküli egyész mutatója). Ekkor az ilyenekből álló p tömb i. elemének elérése: pword(longint(p)+i*sizeof(p^))^, ha az elemeket 0-tól számozzuk. Ezt a módszert azonban csak Turbo Pascalban használjuk, esetlegessége abban rejlik, hogy ha a deklarációnál megváltoztatjuk az adattípust (pl. 2 bájtos helyett 4 bájtosra), akkor minden elemelérésnél meg kell változtatni, és hogy más operációs rendszeren és géptípuson nem garantált, hogy a longint mérete megegyezik a mutató méretével.

Dinamikus tömbök a Free Pascalban

A Free Pascal (az 1.1-es verziótól) beépített dinamikus tömb támogatással rendelkezik. Dinamikus tömb deklarációja: array of elemtípus. A tömb elemeit ugyanúgy érhetjük el, mint egy 0-tól számozott statikus tömb elemeit. Mielőtt a tömböt használjuk, meg kell adnunk az elemszámát a setlength paranccsal: a következő deklaráció után:
var a:array of word;
, a begin és az end között, az utasításblokkban megadjuk a méretét:
setlength(a,100);
Ez lefoglalja a memóriát 100 db word típusú elem számára. A méretet bármikor változtathatjuk. Ha növeljük, az elemek megmaradnak, ha csökkentjük, az utolsó elemek elvesznek. Ez azonban lassú folyamat, mert a gép összes elemet átmásolja az új méretű tömbbe, és a réginek lefoglalt memóriát felszabadítja. Ha a tömböt már nem használjuk, felszabadítjuk a lefoglalt memóriát:
finalize(a);
Ez ekvivalens a következő hívással:
setlength(a,0);

A dinamikus tömb elemszámát a length(a) függvényhívással kérdezhetjük le.

Ha Free Pascalban dinamikus tömböt akarunk átadni paraméterként, vagy visszaadni függvényértékként, annak típusát deklarálni kell, és a deklarált típusnevet kell használni. Az egységesség és az ütközések elkerülése erdekében a Pascal beépített típusainak esetében a deklarációt a types modulba kell helyezni, speciális típus esetén pedig abba, amelyben a típust deklaráljuk.

Ha a formális paraméterlistában array of elemtípus típusú paramétert helyezünk el, "nyitott tömb paramétert" kapunk. A programraktárban az egyszerűség érdekében nem használunk nyitott tömb paramétereket.

Dinamikus tömbök átadása paraméterként

Mind a mutatókkal létrehozott, mind a Free Pascal beépített dinamikus tömb típusával deklarált dinamikus tömb változókban nem maga a tömb, hanem csak az első elem memóriacíme tárolódik. Ha ezt átadjuk egy szubrutinnak érték szerinti paraméterként, akkor a tömb nem kerül lemásolásra, tehát a hívó proigramban és a szubrutinban használt mutató ugyanarra a címre mutat. Így, bár a szubrutin a hívó programban érvényes változót nem változtathatja meg, az általa mutatott memóriaterületen lévő adatokat igen, tehát ha a szubrutinban a paraméterként átadott dinamikus tömbnek megváltoztatja egy elemét, az a hívó programban is megváltozik. Így visszaadhatunk adatokat a függvényből: pl. egy tömbsorbarendező eljárást megírhatunk úgy, hogy paramétere egy dinamukus tömb, és magában a tömbben dolgozik. Ha ezt nem akarjuk, a tömböt elemenként le kell másolni. Ha viszont egy Free Pascal dinamikus tömbnek megváltoztatjuk a méretét, új memóriacíme lesz, tehát ha egy értékparaméterként átadott dinamikus tömb méretét megváltoztatjuk a szubrutinon belül, akkor, mivel a hívó környezetben lévő változóban nem változik meg a cím, a hívó környezetben a tömb elemeinek későbbi elérése hibához vezet. Ha a tömb méretét is meg kell változtatni a szubrutinon belül, cím szerinti paraméterként adjuk át.