Programozás Turbo Pascal nyelven

Tartalomjegyzék:


1. Bevezetés

A Pascal programozási nyelv alapjait Niklaus Wirth definiálta 1971-ben (68?). A Pascal magas szintű, általános célú, struktúrált programnyelv, az Algol és a Fortran legjobb elemeit egyesíti. Szigorú nyelv, egyszerű eszközrendszerrel, szintaktikai és szemantikai szabályokkal. Az oktatás kedvelt nyelve. A Standard Pascal kibővített mikrogépes (elsősorban IBM PC) változata a Borland cég által kifejlesztett Turbo Pascal.
A Pascal szabad formátumú nyelv, azaz a külalaknak (pl. sorhosszúság, bekezdések) csak a program áttekinthetősége szempontjából van jelentősége. A nyelv nem különbözteti meg a kis és nagybetűket.


2. Értékadás, olvasás a billentyűzetről, kiírás a képernyőre

2.1 Értékadó utasítás

változó := kifejezés
függvény azonosító := kifejezés ld. xx fejezet

Példa:

        x := 2 * ( x + y ) + sin(alfa);
A kifejezés kiértékelése során előálló érték kerül a változó címére a memóriába. A kifejezések formálisan operandusokból, műveleti jelekből (operátorok) és kerek zárójeleből épülnek fel. Operandusok konstansok (pl. 2, 'szöveg'), változók (pl. x, alfa) és függvényhívások (pl. sin(alfa)) lehetnek.
Kifejezések kiértékelése:
- Először a zárójelben szereplő kifejezések kerülnek kiértékelésre.
- A műveletek kiértékelésének a sorrendjét a precedenciájuk szabja meg. A precedencia szintek:
  1. NOT, +, -, @ (egy operandusú műveletek)
  2. *, /, DIV, MOD, AND, SHL, SHR
  3. +, -, OR, XOR
  4. <, >, <=, >=, <>, =, IN
- Azonos prioritás esetén a balról jobbra szabály lép érvénybe, amelyet a fordító felülbírálhat az optimális kód készítése érdekében.

2.2 Olvasás a billentyűzetről

A Pascal nyelvben nincs input utasítás, a billentyűzetről a változókba a Read és a ReadLn eljárások segítségével olvashatunk be.

Read(v1 [,v2...])
ReadLn(v1 [,v2...])

A változók numerikus, karaktes és string (karakterlánc) típusúak lehetnek. A részletesebb leírást ld. szöveges állományok.
Példa:

        ReadLn(fizetes);
A program ennél a sornál megáll, a leütött karakterek a képernyőn is megjelennek, és az Enter leütéséig szerkeszthetők. Majd a változóba bekerül a megfelelő típusú érték. Ha a beírtak nem felelnek meg a változó típusának, akkor a program futási hibával leáll (I/O hiba lép fel).

Karakteres, karakterlánc típusú változók olvasásakor a Read használata kellemetlen félreértéseket okozhat, használjuk a ReadLn eljárást. A Read eljárás a billentyűzet pufferból kiolvassa a változónak megfelelő értéket, de nem törli a puffert, míg a ReadLn eljárás olvasás után törli a puffert. Próbáljuk ki a következő programrészletet:

        Read(a);
        Read(b);
        Read(c);
        WriteLn(a);
        WriteLn(b);
        WriteLn(c);

2.3 Kiírás a képernyőre

A Pascal nyelvben nincs output utasítás, a képernyőre a Write és a WriteLn eljárások segítségével írhatunk.

Write(k1 [,k2...])
WriteLn(k1 [,k2...])

Az eljárások az aktuális pozíciótól kezdődően kiírják a kifejezések értékeit. A WriteLn eljárás ezután sort emel. A kifejezések numerikus, karaktes és string (karakterlánc) és logikai típusúak lehetnek. A kiírást módosíthatjuk, mezőszélességet illetve valós kifejezés esetén a tizedesjegyek számát adhatjuk meg: Write(k[:MezSzel[:Tized]]). A mezőben jobbra igazítva, illetve a megfelelő számú tizedesjegyre kerekítva jelenik meg a kifejezés értéke.
Példa:

        Write('A dolgozó fizetése: ', fizetes:10);

3. A Pascal program felépítése

Egy Pascal programot három részre oszthatunk:
  1. Programfej
  2. Programblokk - deklarációs rész (vagy: leíró rész)
  3. Programblokk - végrehajtandó rész (vagy: programtörzs)
Nézzük kicsit részletesebben!

Programfej:

[PROGRAM azonosító;]
[USES azonosító [,azonosító...];]

A PROGRAM fenntartott szó után álló azonosító lesz a programunk neve. Ez célszerűen megegyezhet a forrásprogram, a lemezen tárolt PAS kiterjesztésű állomány nevével. Mindez elhagyható, de használata jaánlott.
A USES kulcsszó után a programunk által használt egységeket soroljuk fel. A System egység, amely a leggyakrabban használt deklarációkat - konstansokat, változókat, eljárásokat, függvényeket - tartalmazza automatikusan hozzászerkesztődik a programunkhoz.

Deklarációs rész:

[LABEL címke [,címke...];]
[TYPE azonosító = típus;...]
[CONST azonosító [: típus] = konstans;...]
[VAR azonosító : típus;...]
[PROCEDURE azonosító [(formális paraméterlista)];
eljárásblokk]
[FUNCTION azonosító [(formális paraméterlista)] : típusazonosító;
függvényblokk]

A későbbiek során részletesebben tárgyaljuk a deklarációs rész egyes elemeit.
A Var kulcsszó után álló változódeklarációs szakaszban a programblokkban használt összes változót fel kell sorolni, és típusát megadni. A típusmegadás történhet áttételesen is, a Type utáni típusdeklaráció segítségével (ld. tömb).
A konstansok használata programozói munkánkat könnyítheti meg (Const).
Címkék igen ritkán fordulnak elő egy Pascal programban (Label).

Végrehajtandó rész:

BEGIN
[utasítás [; utasítás...]]
END.

A Pascal szabad formátumú nyelv, azaz több utasítás is szerepelhet egy sorban vagy akár egy utasítást több sorra is tördelhetünk. Célszerű a program olvashatóságára, áttekinthetőségére törekedni. Az egyes utasításokat ; -vel választjuk el egymástól. (Szemben pl. a C nyelvvel, ahol minden utasítás végére ; -t kell írnunk.)
A Pascal nyelv nem különbözteti meg a kis és nagy betűket (szemben a C nyelvvel).
Programunkban korlátlan hosszúságú megjegyzést helyezhetünk el a { ... } illetve a (* ... *) jelek között.

A program szövegében használhatunk:


4. Egyszerű adattípusok

4.1 Egész típusok

Egy típus jellemzésénél az alábbiakat kell figyelembe venni:
  1. felvehető értékek halmaza (adatábrázolás)
  2. konstansai
  3. végezhető műveletek
  4. szabványos eljárások, függvények
a. A Pascal nyelvben bőséges választék áll a rendelkezésünkre egész típusokból
 
Típus
Értéktartomány
Tárolás
Byte
0..255
1 byte
ShortInt
-128..127
1 byte
Word
0..65535
2 byte
Integer
-32768..32767
2 byte
LongInt
-2*109..2*109
4 byte
A Byte és a Word típus esetén 8 illetve 16 biten 28 = 256 ( 00000000-tól 11111111-ig ) illetve 216 = 65536 különböző számot ábrázolhatunk kettes számrendszerben. A ShortInt, az Integer és a LongInt típusokban negatív számokat is tárolhatunk. Itt az ábrázolási tartomány egyik fele kettes komplemens kódolással negatív számokat jelent.

b. Egész típusú konstans

Decimális egészek, pl. 25, -123
Hexadecimális egészek, pl. $33, -$A2D6

c. Végezhető műveletek

Az eredmény is egész típusú:

+,- (előjel), *, +, -
div egész osztás, pl. 13 div 3 = 4
mod maradékképzés, pl. 13 mod 3 = 1
not bitenkénti negálás, pl not 28 = -29 ; (not 00011100 = 11100011)
and bitenkénti és, pl. 5 and 6 = 4; (00000101 and 00000110 = 00000100)
or bitenkénti vagy, pl. 5 or 6 = 7; (00000101 or 00000110 = 00000111)
xor bitenkénti kizáró vagy, pl. 5 xor 6 = 3; (00000101 and 00000110 = 00000011)
shl bitenkénti eltolás balra, pl. 5 shl 3= 40; (00000101 shl 3 = 00101000)
shr bitenkénti eltolás jobbra, pl. 5 shr 2= 1; (00000101 shr 2= 00000001)

Az eredmény kivezet az egész számok köréből:

/ az eredmény mindig valós (6/2-t már nem egész számként kezeli a rendszer)
<, >, <=, >=, =, <> relációs műveletek, az eredmény logikai típusú
in halmaz elemvizsgálat, logikai eredmény (ld. halmaz adattípus)

d. Fontosabb szabványos eljárások, függvények

Függvények: Abs, Sqr, Trunc, Round, Ord, Pred, Succ, Random.
Eljárások: Inc, Dec, Str, Val, Randomize.

Megj.
A függvények mindig egy értéket állítanak elő (visszatérési érték), kifejezésekben hívhatjuk meg őket, pl. egy értékadó utasítás jobb oldalán; a := Abs(a) + 2 . A függvények paraméteri kifejezések lehetnek, pl. Sqr(a + Round(x)).
Az eljárásokat utasításszerűen hívjuk, pl. Inc(i).

Példaprogram

4.2 Valós típusok

a. Értéktartomány, számábrázolás

A Turbo Pascalban a következő valós típusokat definiálták: Real (6 byte), Single (4 byte), Double (8 byte), Extended (10 byte), Comp (8 byte), azonban a Real típus kivételével mindegyik használatához matematikai társprocesszorra, vagy annak emulálására van szükség.
A Real típus ábrázolása 6 bájton, lebegőpontosan történik. Az értéktartomány:

Smin = 2,9*10-39
Smax = 1,7*1038

A pontosság 11-12 decimális számjegy. (Ennyi értékes számjegye egy valós számnak, a többi jegyet nem ábrázolja a rendszer, pl. a 1234567890,1234567 számból a színes jegyek elvesznek.)

b. Konstansok

Pl. 5.12, -123.2313, 12.54E6, 21.12E-5

c. Műveletek

Az eredmény is valós típusú: +,- (előjel), *, /, +, -
Az eredmény logikai típusú: <, >, <=, >=, =, <>

d. Fontosabb szabványos eljárások, függvények

Függvények: Abs, Sqr, Sqrt, Sin, Cos, ArcTan, Exp, Ln, Int, Frac, Random, Round, Trunc, Pi
Eljárások: Str, Val, Randomize

Példaprogram

4.3 A karakteres - Char típus

a. Értéktartomány, adatábrázolás

Egy bájtos típus, tehát 28 = 256 különböző érték , az ASCII kódrendszer 256 elemének a tárolására képes. A karakter típusú változó egy ASCII kódot tartalmaz. Pl. ha a változóhoz tartozó memóriarekesz értéke 65, akkor mivel változónk típusa Char, ezt a rendszer 'A' betűként értelmezi.

b. Konstansok

Formája: 'A', '*', '4', #65 (ez utóbbi a 65-ös ASCII kodú karaktert, azaz 'A'-t jelenti).
Lehetnek: betűk, számjegyek, írásjelek, speciális karakterek (pl. '@', '#', '$', ...), vezérlő karakterek (pl. a gyakran használt #27 - Escape), egyéb karakterek (az ASCII kódtábla 128-255 része, pl. 'é').

c. Műveletek

Relációs műveletek: <, >, <=, >=, =, <> (az eredmény természetesen logikai típusú, a karakter ASCII kódja határozza meg. Pl. 'A' < 'B' igaz, 'A' = 'a' hamis.)
in halmaz elemvizsgálat, logikai eredmény (ld. halmaz adattípus)

d. Fontosabb szabványos eljárások, függvények

Függvények: Ord, Chr, UpCase, Pred, Succ
Eljárások: Inc, Dec

Példaprogram

4.4 A logikai - Boolean típus

a. Értéktartomány, adatábrázolás

Egy logikai típusú változó két értéket vehet fel: igaz vagy hamis. Ábrázolására egy bájton történik (akár egy bit is elég lenne). Ha a bájt értéke 0, akkor a logikai típusúként értelmezett érték hamis, nullától eltérő érték esetén pedig igaz.

b. Konstansok

Két előredefiniált konstans: True (igaz), False (hamis)

c. Műveletek

Az eddig tanult típusokra értelmezett relációs operátorok (valamint az in halmazművelet) mindig logikai értéket állítanak elő.

Logikai műveletek: not, and, or, xor (az operandusok logikai típusúak). A műveletek igazságtáblái:
 
NOT
AND
OR
XOR
A
not A
A
B
A and B
A
B
A or B
A
B
A xor B
False
True
False
False
False
False
False
False
False
False
False
True
False
False
True
False
False
True
True
False
True
True
True
False
False
True
False
True
True
False
True
True
True
True
True
True
True
True
True
False
Pl. egy logikai kifejezés: (x > 4) and (x < 10) or not b , ahol b egy Boolean változó.

d. Logikai értéket előállító függvények

Odd, Eof, Eoln

Példaprogram

4.5 Felsorolt típus

a. Értékek, adatábrázolás

A típus megadásakor fel kell sorolnom a lehetséges értékeit. Ezek csak azonosítók lehetnek.
A konstansok a felsorolás sorrendjében sorszámot kapnak 0-tól kezdődően. Tarolás a konstansok számától függően 1 vagy 2 bájton történik.

Pl.
var tantargy: (magyar, matek, fizika, tesi);
...
tantargy := matek;

b. Konstansok

Konstansai a felsorolásban szereplő azonosítók.

c. Műveletek

Relációs műveletek:<, >, <=, >=, =, <> (az eredmény természetesen logikai típusú, a felsorolt típusú érték sorszáma határozza meg. Példánkban: matek < tesi igaz.)
in halmaz elemvizsgálat, logikai eredmény (ld. halmaz adattípus)

d. Függvények, eljárások

A sorszámozás alapján értelmezhetőek az alábbi
függvények:Ord, Pred, Succ;
eljárások: Inc, Dec.

4.6 Intervallum típus

Egy már létező sorszámozott típus egy intervalluma. Szintén nekünk kell definiálnunk.
Az adatábrázolás, műveletek, függvények, eljárások megegyeznek az eredeti típuséval.

Pl.

  var  nap: (hetfo, kedd, szerda, csutortok, pentek, szombat, vasarnap); {felsorolt típus}
       munkanap: hetfo..pentek;
       betu: 'A'..'Z';
       szamjegy: '0'..'9';
A felsorolt típus használata a program tesztelésekor hasznos lehet. (A fenti példában szamjegy := 'e' fordítási hibát, szamjegy beolvasásakor az 'e' karakter pedig futási hibát eredményez ($R+ fordítási direktíva esetén).)

4.7 Az egyszerű típusok csoportosítása

Sorszámozott típusok: minden lehetséges értékhez hozzárendelhető egy sorszám, az értékek a sorszám szerint rendezettek.


5. Algoritmus elemek

5.1 Ciklusok - iterációk

Segítségével utasítás(ok) ismételten végrehajható(k). Részei az utasítást (utasításokat) tartalmazó ciklusmag és az ismételt folytatást vagy befejezést vezérlő rész.

A Pascal nyelv három ciklust definiál: két feltételes ciklust valamint egy előírt lépésszámú (más néven számláló vagy léptető) ciklust.

5.1.1 A WHILE ciklus

Az utasítás szintaktikája:

WHILE feltétel DO utasítás

Kezdőfeltételes ciklus. Amíg a feltétel igaz, addig ismétli az utasítást, ha hamis, akkor a program következő utasítására ugrik. A feltétel egy logikai (boolean) kifejezés. Ha több utasítás szerepel a ciklus magjában, akkor Begin - End ún. utasítás zárójelet kell alkalmaznunk. Ha a feltétel már először sem teljesül, akkor a ciklusmag egyszer sem kerül végrehajtásra (üres ciklus), ha pedig a feltétel soha nem vesz fel hamis értéket, akkor a program végtelen ciklusba kerül.

Tipikus használata: a ciklus végrehajtása attól függ, hogy van-e még feldolgozandó adat, az ismétlések számát előre nem ismerjük, akár 0 is lehet.

Példa: Képezzük a billentyűzetről érkező pozitív számok összegét, a számsorozat végét a 0 vagy egy negatív szám jelezze.
Megoldás
Az ilyen típusú feladatok általános megoldási sémája:
- az első adat előállítása (pl. beolvasása)
- ciklusfej
- az adat feldolgozása
- a következő adat előállítása (pl. beolvasása)
- ciklus vége

5.1.2 A REPEAT ciklus

Az utasítás szintaktikája:

REPEAT [utasítás [; utasítás...]] UNTIL feltétel

Végfeltételes ciklus. Az utasítás(ok) végrehajtását meg kell ismételni, ha a feltétel hamis. Ha a feltétel igaz, a program a ciklus utáni utasítással folytatódik.
A ciklusmag legalább egyszer végrehajtódik. A ciklusmagot a Repeat - Until kulcsszavak fogják közre, nem kell Begin - End utasítás zárójelet használnunk.

A While és a Repeat ciklust hasonló típusú feladatok megoldására használhatjuk. Esetleg az egyik egy kicsit kényelmesebb megoldást nyújt.

Az előző fejezet (5.1.1) példájának megoldása Repeat-Until ciklus alkalmazásával.

5.1.3 A FOR ciklus

Az utasitás szintaktikája:

FOR ciklusváltozó := kezdőérték TO / DOWNTO végérték DO utasítás

ahol a ciklusváltozó sorszámozott típusú változó hivatkozás, a kezdőérték és a végérték pedig sorszámozott típusú kifejezések.
A ciklusváltozó a kezdőértéktől a végértékig egyesével nő (To) vagy csökken (DownTo). Az utasítás (vagy a Begin - End utasítás zárójelek közé zárt utasításcsoport) a ciklusváltozó minden értékénél végrehajtódik. Elöltesztelő ciklus, így ha a kezdőérték > végérték (To esetén) vagy a kezdőérték < végérték (DownTo esetén), akkor a ciklusmag egyszer sem kerül végrehajtásra (üres ciklus).
A ciklusváltozó értékét a ciklusmagban felhasználhatjuk, de nem változtathatjuk meg (egy elrettentő példa).

Tipikus használata: az ismétlések száma a ciklusba való belépés előtt már ismert vagy kiszámítható.

Példák:
1. Írjunk ki N darab csillagot a képernyőre!
Megoldás
2. Számoljuk ki N! értékét!
Megoldás

5.2 Elágazások - szelekciók

A ciklus mellett a másik alapvető algoritmus elem az elágazás vagy szelekció, amelyben lehetőség van több tevékenység közül egyet kiválasztani.

5.2.1 Az IF utasítás

Az utasítás szintaktikája:

IF feltétel THEN utasítás1 [ELSE utasítás2]

ahol a feltétel egy logikai kifejezés.
Ha a feltétel igaz, akkor az utasítás1 hajtódik végre, egyébként az utasítás2. Az Else ág elhagyható, ilyenkor az utasítás1 kimarad, a program a következő utasítással folytatódik. Egy ágon több utasítás is végrehajtható a Begin - End utasítás zárójelek alkalmazásával. Vigyázzunk, az Else előtt a pontosvessző szintaktikai hiba, mivel azzal lezárjuk a teljes If utasítást!

Példa:

        ReadLn(Oszto);
        ReadLn(Osztando);
        if Oszto <> 0 then 
          Hanyados := Osztando / Oszto
        else
          WriteLn('0-val való osztás, nincs értelmezve.');
Akár az utasítás1 vagy az utasítás2 is lehet újabb If utasítás, és ezáltal többirányú elágazást is megvalósíthatunk.
Példák az If utasítások egymásba ágyazására.

5.2.2 A CASE utasítás

Az utasítás szintaktikája:

CASE szelektor OF
állandó [..állandó] [,állandó[..állandó]...] : utasítás;
[állandó [..állandó] [,állandó[..állandó]...] : utasítás;
[ELSE utasítás]
END

ahol a szelektor egy sorszámozott típusú kifejezés. Abban az ágban lévő utasítás (vagy Begin - End közé zárt utasításcsoport) hajtódik végre, ahol a szelektor értéke megegyezik az egyik állandóval vagy beleesik az egyik megadott tartományba. Ha ez egyik esetre sem teljesül, akkor az Else ágra kerül a vezérlés. Ez utóbbi elhagyható.

Példaprogram


6. Karakterlánc típus valamint a strukturált (összetett) típusok

6.1 A karakterlánc - String típus

Deklarálása: STRING[[maxhossz]]

ahol 1 <= maxhossz <= 255, ha elhagyjuk, maxhossz = 255. Dinamikus hosszúságú karaktersorozat: hossza futás közben változhat, elemei Char típusúak.

Karakterlánc konstans: 'aposztrófok közötti szöveg'

Hivatkozhatunk a karakterlánc egy elemére (amely Char típusú):
azonosító[index], pl. karlanc[5].

A karakterlánc típusú változó értékét megváltoztathatjuk értékadással, valamint beolvasással. Értékét kiírathatjuk a képernyőre.
Pl. karlanc := 'hahó'

Műveletek:

+ összefűzés, pl. file_spec := utvonal + file_nev;
Relációs műveletek: <, >, <=, >=, =, <> (az eredmény természetesen logikai típusú, az első nem egyenlő karakter ASCII kódja határozza meg. Pl. 'ATTILA' < 'ALFONZ' hamis.)

Adatábrázolás:

A rendszer a karakterek ASCII kódját tárolja maxhossz+1 bájton. A karakterlánc 0. bájtja egy tartalmazza a karakterlánc hosszát, Ord(karlanc[0]) = Length(karlanc).

Szabványos függvények, eljárások:

Függvények: Length, Pos, Copy, Concat.
Eljárások: Str, Val, Delete, Insert.

Lehetőleg a string műveleteket és függvényeket használjuk a karakterláncok kezelésére, a karakterenkénti hozzáférést körültekintően végezzük. (Egy így fellépő hiba.)

Példa:
Olvassunk be egy modatot, és írjuk ki csupa nagybetűvel!
Megoldás

6.2 A tömb - Array típus

A tömb strukturált (összetett adattípus).

Deklarálása: ARRAY[indextípus [,indextípus...]] OF elemtípus

Pl.
vektor: array[1..10] of integer; {Tíz elemű egydimenziós tömb.}
matrix: array[1..5, 1..4] of integer; {5 sorból és 4 oszlopból álló kétdimenziós tömb, mátrix, elemei integer típusúak.}

A tömb olyan adatcsoport, melynek elemei azonos típusúak, az elemek száma rögzített (statikus méretű), sorrendjük kötött (indexelés), az elemekhez közvetlenül hozzáférhetünk. Meghatározása: név, dimenzió, elemeinek típusa, indexeinek típusa és tartománya.

Az indextípus csak sorszámozott típus lehet (kivéve Longint), többnyire intervallum típus,
pl.
array[1..10] of real {általában egész típus intervalluma}
array['a'..'z'] of real {ritkábban karakteres típus intervalluma}
array[byte] of real {még ritkábban egy előre definiált típus}.

Többdimenziós tömbök: a tömb elemei maguk is tömbök,
pl.
m: array[1..10, 1..20] of integer,
vagy
m: array[1..10] of array[1..20] of integer.

A hivatkozás a tömb egy elemére az index segítségével történik,
pl.
vektor[3] {a vektor nevű tömb 3. eleme, Integer típusú},
matrix[2, 3] vagy matrix[2][3] {a matrix nevű kétdimenziós tömb 2. sorának 3. eleme}.

Műveletek:

Két azonos típusú tömbre az értékadás és az egyenlőség vizsgálat megengedett. Egy vektor bemásolható a mátrix egy sorába (ld. tömb típus deklarálása).

A tömbökkel végzett műveletek során többnyire a for ciklust használjuk, úgy hogy a ciklusváltozót végigléptetjük a tömb indextartományán.
Pl. egy a vektor nevű tömb beolvasása a billentyűzetről:

for i := 1 to 10 do 
  begin 
    Write('Kérem a tömb ',i,'. elemét: ');
    ReadLn(t[i])
  end;
Tömb típusú konstans:

Tömb konstanst csak a Const deklarációs részben adhatunk meg tipizált konstansként. Az elemeket zárójelben, vesszővel elválasztva kell felsorolnunk.
Pl.
const T1 : array[1..3, 1..4] of byte = ( (1, 3, 4, 1), (2, 3, 4, 2), (1, 6, 3, 5) );

Adatábrázolás:

A rendszer a tömböt sorfolytonosan tárolja a memóriában. A foglalt terület az elemek száma szorozva egy elem méretével.

Tömb típus deklarálása:

Tömb típusú változók használatakor célszerű először a Type típusdeklerációs részben a megfelelő típusokat deklarálni, majd ezeket a saját típusainkat használhatjuk a változók deklarálásakor (a Var után). A Pascal nyelv logikája ezt az áttételes deklarálást támogatja, és bizonyos esetekben ez nem is kerülhető meg (ld. alprogramok).
Pl.

type VektorTip = array[1..4] of integer;
     MatrixTip = array[1..5] of VektorTip;
var  v1, v2: VektorTip;
     m1, m2: MatrixTip;
A fenti példában elvégezhető a következő értékadás: pl. m1[2] := v1.

Példák:
1. Töltsünk fel egy 10 elemű integer tömböt. Számítsuk ki az elemek számtani átlagát.
Megoldás
2. Állítsuk elő a Fibonacci sorozat (1, 1, 2, 3, 5, 8, 13...) első 20 elemét.
Megoldás

6.3 A rekord - Record típus

A rekord strukturált adattípus, amelyben különböző típusú adatokat (mezőket) fogunk össze.

Deklarálása:

Deklarálásakor a Record és End kulcsszavak között fel kell sorolnunk az egyes mezők neveit valamint típusait. A rekord tartalmazhat egy változó részt is, amely a fix rész egy mezőjétől (szelektor mező) függően más-más adatokat tartalmazhat.

RECORD
[mezőlista;]
[CASE szelektormező: sorszámozott típus OF
állandók: (mezőlista)
[állandók: (mezőlista)...]]
END

ahol mezőlista:
mezőazonosító [,mezőazonosító...]: típus
[;mezőazonosító [,mezőazonosító...]: típus...]

Példák:

1.
type DolgozoTip = record
                    Nev: string;
                    Hazas: boolean;
                    Fizetes: longint;
                  end;
var Dolgozok: array[1..50]of DolgozoTip;
    d1, d2: DolgozoTip;

2.
type RekTip = record
                Nev: string[50];
                Lakohely: string[50]
                case Nagykoru: boolean of 
                  True: (Gyerek_Szam: byte);
                  False: (Anyja_Neve: string[50];
                          Apja_Neve: string[50]);
              end;
Hivatkozás a rekord egy mezőjére:
rekordazonosító.mezőazonosító
Pl.
d1.Nev
Dolgozok[5].Hazas

A With utasítás

Ha egy programrészletben gyakran hivatkozunk egy (vagy több) rekord mezőire, akkor a With utasítással ez leegyszerűsíthető, a rekordazonosító elhagyható.

Szintaktikája: WITH rekordazonosító [, rekordazonosító] DO utasítás
Pl.

with d1 do
  begin
    ReadLn(Nev);
    ReadLn(Fizetes)
  end;
Ha több rekordazonosítót sorolunk fel, akkor az utolsónak a legnagyobb a prioritása.

Rekord típusú konstans:

Hasonlóan a tömb konstanshoz csak tipizált konstans kezdőértékeként adhatjuk meg a Const deklarációs részben.
Pl.

const Origo: record
               x, y: integer;
             end = (x: 320; y: 240);
Adatábrázolás:

A memóriában elfoglalt hely egyszerűen a mezők helyfoglalásának az összege (fix rész + legnagyobb változó rész).

6.4 A halmaz - Set típus

A programozásban a halmaz azonos típusú különböző elemek összességét jelenti. A halmazt az elemek felsorolásával adhatjuk meg. Az elemek rendezetlenek. Az összetett adattípusokhoz soroljuk, bár a halmaz egy elemére nem tudunk hivatkozni.

Deklarálása: SET OF alaptípus

ahol az alaptípus csak olyan sorszámozott típus lehet, amelynek maximálisan 256 eleme van.

Halmaz típusú konstans:

Szögletes zárójelben a halmaz elemeit (az alaptípussal megegyező típusú konstansokat) vagy azok intervallumait felsoroljuk.
Pl.
const Betuk = ['A'..'Z', 'a'..'z'];

H1 := [1, 4, 6, 8..12]
H2 := [] {üres halmaz}

A programunkban egy halmaznak a halmaz konstanstól egy kicsit különböző halmazkonstruktorral is értéket adhatunk. Itt a szögletes zárójelben felsorolt kifejezések változókat is tartalmazhatnak.
Pl.
k := '?';
H := [k, '!','.'];

Műveletek:

* metszet
+ egyesítés
- különbség
Logikai típusú eredményt szolgáltatnak:
= egyenlőség
<> különbözőség
<=, >= tartalmazás (részhalmaz)
IN elemvizsgálat (a következő példában a Char típusnál látott programot pontosítjuk a segítségével)

Adatábrázolás:

Egy lehetséges halmazelemnek egy bit felel meg a memóriában. Ha az lehetséges elem benne van a halmazban, akkor a bit 1, ellenkező esetben 0. Gyakran az első és az utolsó bájt olyan biteket is tartalmazhat, amelyek nem vesznek részt a tárolásban.

Példa:
A halmazokat gyakran használjuk arra, hogy menüből való választás esetén programunk csak a megfelelő billentyűkre reagáljon. Egy menü váza.


7. Alprogramok

Az alprogram olyan utasítások csoport, amelyet a program bizonyos pontjairól aktivizálhatunk. Az alprogramokat a deklarációs részben kell megírnunk (procedure, function kulcsszavak után). Az alprogramok tartalmazhatnak újabb alprogramokat (egymásba ágyazás).
Akkor használjuk őket, ha
- bizonyos tevékenység többször előfordul a programban,
- egy nagy programot tagolni, strukturálni szeretnénk.
Két fajtája van:
- eljárás (procedure): egy tevékenységcsoportot hajt végre, utasításszerűen hívjuk (ld. standard eljárások),
- függvény (function): feladata egy érték előállítása, hívásakor neve operandusként egy kifejezésben szerepelhet (ld. standard eljárások).
Már eddig is sok szabványos eljárást és függvényt használtunk, melyeket a különböző egységekben deklaráltak. Nézzük, hogyan készíthetünk saját alprogramokat!

7.1 Eljárás - Procedure

a, Szerkezete:

Hasonló a programéhoz.
 
Eljárás fej: PROCEDURE azonosító [ ( formális paraméter lista ) ];
Deklarációs rész: 
 
 
 
Label... 
Const... 
Type... 
Var... 
Procedure... 
Function...
Végrehajtandó rész: Begin 
utasítások 
End;
ahol, formális paraméter lista:
[Var] azonosító [, azonosító...] : típusazonosító [; [Var] azonosító [, azonosító...] : típusazonosító...]

Például:

procedure Teglalap(a, b: integer; var t, k: integer); 
  begin 
    t := a * b;
    k := 2 * (a + b) 
  end;
b, Az eljárás hívása:

azonosító [ (aktuális paraméter lista)]

ahol az aktuális paraméter lista elemei kifejezések vagy változók lehetnek (ld. paraméterátadás) egymástól vesszővel elválasztva.

Pl. Teglalap(5, 4, Ter, Ker)

c, Az eljárások hatásköre:

A Pascal nyelv befelé struktúrált, az alprogramokat egymásba ágyazhatjuk (az alprogram deklarációs részében is lehet alprogram). Ezért fontos tisztán látnunk, hogy hogy a főprogramból illetve egy alprogramból mely eljárásokat hívhatjuk meg:
- A program illetve egy eljárás meghívhatja (ismeri) azokat az alprogramokat, melyeket a program vagy az adott alprogram deklarációs részében deklaráltunk, de azok alprogramjait már nem.
- Egy alprogram meghívhatja az ugyanazon deklarációs részben (, ahol őt deklaráltuk) korábban deklarált alprogramokat.
- Egy alprogram meghívhatja az őt tartalmazó eljárásokat.
- Egy alprogram meghívhatja saját magát.

Nézzük a fenti eseteket egy példán keresztül.

d, Paraméterek:

A paraméterek az eljárás és az őt hívó programrész közötti adatcserét, kommunikációt szolgálják. A formális paraméterekkel írjuk le az alprogram tevékenységét. Híváskor ezek helyére konkrét objektumokat, aktuális paramétereket írunk.
Az aktuális és a formális paramétereknek meg kell egyezniük számban, sorrendben és típusban.
Vigyázzunk, hogy a formális paraméterek típusának megadásakor csak típusazonosítót használhatunk, így pl. a következő eljárásfej hibás: procedure elj (t: array[1..10] of real);

A paraméterátadás két féle módon történhet:

- Érték szerinti paraméter átadás (a deklarációban a formális paraméter előtt nincs Var)

Ekkor az aktuális paraméter értéke kerül át a formális paraméterbe. Az eljárás minden egyes hívásakor a rendszer tárterületet rendel a verem memóriában a formális paraméterekhez, és ide másolja be az aktuális paraméterek értékeit. Az eljárás végeztével ez a terület felszabadul. Az aktuális paraméter értékét az eljárás nem változtathatja meg, így ez csak bemenő paraméter.
Az aktuális paraméter kifejezés lehet.

- Cím szerinti paraméter átadás (a deklarációban a formális paraméter elé Var -t írunk)

Az aktuális paraméter címe kerül át a formális paraméterhez, ha változik a formális paraméter, akkor változik az aktuális is. Ezáltal egyaránt használhatjuk be- és kimenő paraméterként is.
Az aktuális paraméter csak változó lehet.

e, Lokális és globális változók

Egy eljárásban deklarált változókat ezen eljárás lokális változóinak nevezzük. Ezek a program más részein nem ismertek. (Így különbőző eljárásokban előfordulhatnak azonos nevű változók, amelyeknek azonban semmi közük egymáshoz.) A lokális változókhoz (az eljárás paramétereihez hasonlóan) a rendszer a veremben rendel tárterületet, dinamikus módon, azaz csak akkor van címe a változóknak, ha az eljáráson van a vezérlés. A lokális változók értéke az eljárás két hívása között elvész.

Egy eljárásra nézve globális változó egy őt tartalmazó eljárásban vagy a főprogramban deklarált változó. Ezt az eljárás ismeri, hacsak nem deklaráltunk egy vele azonos nevű lokális változót vagy az eljárásnak nincs egy vele azonos nevű paramétere. Ekkor a lokális változó "eltakarja" a globálisat. A globális változó értéke természetesen nem vész el. (Abban az esetben használhatunk egy a lokálissal azonos nevű globális változót, ha az a főprogram változója. Ekkor a program nevével kell minősítenünk a globális változót: programnév.változónév.)

Egy példa a lokális és globális változók értelmezésére.

f, Információ csere

Összefoglalva elmonhatjuk, hogy egy alprogram kétféle módon kommunikálhat az őt hívó programegységgel,
- a paramétereken keresztül,
- a globális változók segítségével.

Példák a kétféle adatcserére.

7.2 Függvény - Function

A függvény feladata egy érték előállítása. Ezt az értéket a függvény nevéhez rendeljük, a függvény törzsében kell szerepelni legalább egy értékadó utasításnak, amelyben a függvény neve a baloldalon áll. (Vigyázzunk, ha a jobboldalon szerepeltetjük a függvény nevét, akkor az már rekurziót jelent. Példa egy ilyen hibára.)
A függvényt egy kifejezésben hívhatjuk meg, pl. egy értékadó utasítás jobboldalán.
Szerkezete megegyezik az eljáráséval azzal a különbséggel, hogy még meg kell határoznunk a viszatérési érték típusát is. Így a függvény feje:

FUNCTION azonosító [ ( formális paraméter lista ) ]: típusazonosító;

ahol a típusazonosító csak csak sorszámozott, valós, karakterlánc vagy mutató lehet.

Pl.

function Tangens(Alfa: real): real;
begin
  if cos(Alfa) <> 0 then 
    Tangens := Sin(Alfa) / Cos(Alfa)
end;

7.3 Rekurzió

Ha egy alprogram saját magát meghívja, akkor rekurzióról beszélünk. Megkülönböztethetünk közvetlen és közvetetten rekurziót.

A rekurzió alkalmazásának egyik területe, amikor úgy oldunk meg egy problémát, hogy visszavezetjük egy egyszerűbb esetre, majd ezt addig folytatjuk, míg el nem jutunk a triviális esetig. A módszer a matematikai indukción alapszik.
A megoldás lépései:
1. Megkeressük azt a legegyszerűbb esetet, ahol a megoldás már magától értetődő - triviális eset. Ekkor áll le a rekurzív hívások sorozata.
2. Megvizsgáljuk, hogy ismételt egyszrűsítésekkel hogyan juthatunk el a triviális esethez. (Az általános esetet visszavezetjük az eggyel egyszerűbbre.)

Példák:
1. Faktoriális számítás
- Triviális eset: 1! = 1
- Egyszerűsítés: N! = N*(N-1)!
Ezzel a problémát megoldottuk, már csak kódolnunk kell.
Megoldás
Megj.: Bár a feladat kitűnő példa a rekurzív algoritmusra, az iterációs (ciklussal történő) megodás jobb, mivel az ismételt függvényhívások időigényesek.
2. Fibonacci sorozat (1, 1, 2, 3, 5, 8, 13...) N. eleme
- Triviális eset: az első és a második elem értéke 1.
- Egyszerűsítés: az N. elem az N-1 - edik és az N-2 - dik elemek összege.
Megoldás


8. Állománykezelés

A programok a bemeneti adataikat nem csak a billentyűzetről, hanem a háttértárolókon lévő állományokból is kaphatják, valamint kimeneti adataikat a képernyőn történő megjelenítés mellett állományokban is tárolhatják. A Pascal nyelvben három összetett típus és az ezekhez kapcsolódó szabványos eljárások és függvények valósítják meg az állományok kezelését.

8.1 Típusos állomány

Deklarálása: FILE OF alaptípus

Összetett típus, fizikailag egy lemezes állomány. Egyforma méretű elemekből (komponensekből) áll. Az elemek számának csak a lemez mérete szab határt.
A típusos állományból való olvasás illetve az állományba való írás egysége a komponens.
Az elemekhez a rendszer sorszámot rendel 0-tól kezdődően. Az elérés szekvenciálisa (Read, Write) vagy a komponensek sorszáma szerint direkt módon történhet (az állomány mutató mozgatásával).

A program mindig egy logikai állományt kezel, melyet hozzá kell rendelnünk egy fizikai állományhoz (Assign), majd használat előtt meg kell nyitnunk. A Rewrite eljárás létrehozza, és megnyitja a logikai fájlhoz rendelt fizikai állomány. Ha a fizikai fájl már létezett, akkor törli annak tartalmát. A Reset eljárással egy már létező állományt nyithatunk meg. Ekkor az állománymutató az 0. komponensre áll. (Ezért ezt az eljárást használhatjuk egy nyitott állomány elejére való ugrásra is.) Használat után a Close eljárással zárjuk le fájlunkat!

A típusos állományból a Read eljárás olvas be változókba adatokat. Ügyeljünk arra, hogy a változó típusa egyezzen meg a fájl alaptípusával! Beolvasás után az állomány mutató automatikusan a következő komponensre lép (szekvenciális elérés). Egy változó (vagy kifejezés) értékét a Write eljárással írhatjuk ki egy fájlba. Hasonlóan az olvasáshoz a változó típusának meg kell egyeznie a fájl elemeinek a típusával, valamint az eljárás után az állomány mutató továbblép. Ha az állomány mutató a fájl végén (az utolsó elem mögött) áll, akkor az Eof függvény értéke True. Nézzünk egy példát a fájl szekvenciális feldolgozására:

    Reset(f)
    while not Eof(f) do
      begin
        Read(f,v);
        {a v változóban lévő adat feldolgozása}
      end;
Az állomány mutató direkt pozicionálását a Seek eljárás valósítja meg. A FilePos függvénnyel lekérdezhetjük az aktuális pozíciót, a FileSize függvény pedig az állomány elemeinek a számát (méretét) adja vissza. Példák a pozicionálásra.

Az I/O műveletek során nagy a hibalehetőség (pl. a lemezegység, fájl nem elérhető). Az esetleges futási hibákat tudnunk kell kezelni, ha megbízhatóan működő programot szeretnénk írni. Ha az I/O műveletek ellenőrzése aktív (ez az alapértelmezés), akkor programunk futási hibával leáll egy I/O hiba esetén. Ezért I/O műveletek ellenőrzését inaktívvá kell tennünk a {$I-} fordítási direktívával a kényes műveletek esetén. A művelet után az esetleges hiba kódját az IOResult függvénnyel kérdezhetjük le. Erre egy példa:

    Assign(f, 'adatok.dat');
    {$I-}
    Reset(f);                 {megpróbáljuk megnyitni a fájlt}
    {$I+}
    if IOResult <> 0 then     {ha hiba történt, tehát a fájl nem létezik, }
      Rewrite(f);             {akkor létrehozzuk az állományt}
A Truncate eljárással levághatjuk a fájl komponenseit az aktuális pozíciótól kezdődően.

Lezárt állományokra használhatjuk a Rename valamint az Erase eljárásokat a fájlok átnevezésére illetve törlésére.

Példa:
1. A program egy bolt árucikkeinek adatait (név, kód, ár) tárolja és kezeli egy állományban.
Megoldás

8.2 Szöveges állomány - Text

Deklarálása: TEXT

A Pascal programban szöveges állományként kezelhetjük az egyszerű ASCII szövegeket. (Például a .pas kiterjesztésű forrásprogramjainkat.) A szöveges állomány változó hosszúságú sorokból áll, melyeket a sorvégjel zár le (CR/LF). Az állományt az állományvégjel zárja(^Z). Az Eoln illetve az Eof függvény értéke True, ha az aktuális pozíció egy sorvégjelen vagy az állomány végén áll. A SeekEoln illetve a SeekEof függvények az állomány következő TAB szóköz illetve TAB szóköz és sorvégjel karaktereit átugorva tájékoztatnak arról, hogy sorvégjelen illetve az állomány végén állunk-e.
A szöveges állományt csak szekvenciálisan érhetjük el. Az állomány csak olvasásra vagy csak írásra lehet megnyitni. Az állományból olvasni a Read, ReadLn, illetve írni a Write, Writeln eljárásokkal tudunk. Ha az eljárásoknak a fájl azonosító paraméterét elhagyjuk, akkor az olvasás / írás az alapértelmezett input / output szöveges állományból / -ba történik, ami a billentyűzet illetve a monitor. Szöveges állományból (azaz a billentyűzetről is) olvashatunk egész, valós, karakteres és sztring típusú változokba adatokat. Az állományba az előbbi típusokon kívül még logikai értéket is kiírathatunk.
Az fizikai állományhoz az Assign eljárással rendelhetünk egy Text típusú változót, azaz a logikai állományt. A Rewrite eljárás csak írásra nyitja meg a szöveges állományt, ha nem létezett létrehozza, egyébként törli a tartalmát. A Reset eljárással csak olvasásra nyithatunk meg egy már létező fájlt. Az Append eljárás egy létező állományt nyit meg írásra, és az állománymutató a fájl végére állítja. Az állományt a Close eljárással zárhatjuk be.
Az I/O műveletek hibakódját az IOResult függvény adja vissza (bővebben ld. Típusos állományok).
Lezárt állományokra használhatjuk a Rename valamint az Erase eljárásokat a fájlok átnevezésére illetve törlésére.
A Fluss és a SetTextBuf eljárásokkal az írás, olvasás során a rendszer által használt átmeneti tárolóhoz (pufferhez) férhetünk hozzá.

Példa:
1. A doga.txt állományban egy feladatsor van, kérdések és válaszok felváltva egymás után. Minden kérdés illetve válasz új sorban kezdődik. A kérdések egy számjeggyel kezdődnek, és kérdőjellel fejeződnek be. Készítsünk két új szöveges állományt úgy, hogy az egyik csak a kérdéseket, a másik pedig csak a válaszokat tartalmazza.
Megoldás

8.3 Típusnélküli állomány

Deklarálása: FILE

Általában gyors adatmozgatás vagy ismeretlen állomány esetén használjuk. Hasonló a típusos állományhoz, de az elemeinek nem a típusa, hanem a hossza a lényeges. A komponensek hosszát a fájl megnyitásakor adhatjuk meg (Reset, Rewrite), az alapértelmezés 128 bájt. Az állomány írható, olvasható, az elérés szekvenciálisan (BlockRead, BlockWrite eljárásokkal) vagy az elemek sorszáma szerint direkt módon történhet.

További függvények, eljárások: Assign, Close, Eof, Erase, FilePos, FileSize, IOResult, Rename, Seek, Truncate

Példa:
1. Tördeljünk szét egy állományt egy kilobájt hosszúságú kisebb állományokra!
Megoldás


9. Karakteres képernyő kezelése - a CRT unit

A Crt egység a karakteres képernyő, a billentyűzet valamint a hangszóró kezelését segítő függvényeket, eljárásokat tartalmazza. Mint az egységek többsége, a Crt unit is definiál konstansokat, változókat.

Színek:

A karakteres képernyő tartalma megtalálható az ún. képernyő memóriában. Itt egy karaktert két bájton tárol el a rendszer, melyek a karakter ASCII kódja (1 bájt) valamint a karakter attribútuma (1 bájt). Ez utóbbi a színinformációt hordozza, az alábbi módon:

7 6 5 4 3 2 1 0
V R G B I R G B

A 0.-3. bit a karakter tintaszínét határozza meg, R, G, B az additív színkeverés három alapszíne, I pedig az intenzitás. Például 0100 - piros, 1100 - világospiros, 0101 - lila. A 4.-6. bitek a karakter háttérszínét kódolják. Ha a 7. bit (V) egyes, akkor a karakter villog.
A fentiekből következik, hogy összesen 16 tinta- és 8 háttérszínt használhatunk. A színek kódjait könnyen kiszámolhatjuk, ezeket a megfelelő eljárásokban használhatjuk, de a könnyebb megjegyezhetőség kedvéért a Crt unit az alábbi szín konstansokat definiálja.
 
Tinta- és háttérszínek:  További tintaszínek: 
Balck
0
Fekete DarkGray
8
Sötétszürke
Blue
1
Kék LightBlue
9
Világoskék
Green
2
Zöld LightGreen
10
Világoszöld
Cyan
3
Türkiz LightCyan
11
Világostürkiz
Red
4
Piros LightRed
12
Világospiros
Magenta
5
Lila LightMagenta
13
Világoslila
Brown
6
Barna Yellow
14
Sárga
LightGray
7
Világosszürke White
15
Fehér
Blink 128 Villogás

Pl: TextColor(Lightred+Blink), ezzel egyenértékű: TextColor(12 + 128) vagy TextColor(140).

Fontosabb eljárások, függvények:

Képernyőkezelés:
Függvények: WhereX, WhereY
Eljárások: TextBackground, TextColor, ClrScr, CrlEol, DelLine, InsLine, GotoXY, Window, NormVideo, TextMode

Billentyűzetkezelés:
Függvények: KeyPressed, ReadKey

Hang, késleltetés:
Eljárások: Sound, Delay, NoSound

Példa:
1. Mozgassunk egy téglalapot (egy kis képernyőt) benne egy szöveggel a képenyőn a kurzormozgató billentyűk segítségével!
Megoldás


10. A Turbo Pascal grafikája - a GRAPH unit

Ha rajzolni szeretnénk a képernyőre, akkor azt át kell kapcsolnunk grafikus üzemmódba. A grafikus képernyőn külön-külön hozzáférhetünk az egyes képpontokhoz. Ismernünk kell (illetve bizonyos határok között meghatározhatjuk) a képernyőnk felbontását (a VGA üzemmódban a legnagyobb felbontás 640x480) valamint azt, hogy hány színt használhatunk (az előbb említett felbontásnál 16 színt). A (0, 0) képpont a képernyő bal felső sarkában található.

Az egység fontosabb eljárásai, függvényei, típusai, konstansai:

A grafikus képrnyő inicializálása (átkapcsolás karakteres képernyőről grafikusra), bezárása:
Eljárások: InitGraph, DetectGraph, CloseGraph, stb.
Függvények: GraphResult, stb.
Konstansok: grafikus meghajtók (Pl. Detect = 0, CGA = 1 stb.); grafikus üzemmódok (pl. VGALo, VGAMed, VGAHi stb.)
Pl.:

    uses Graph;
    var Meghajto, Uzemmod: integer;
    begin
      Meghajto := Detect; {Automatikusan beállítja grafikus üzemmódot a legnagyobb felbontással.}
      InitGraph(Meghajto, Uzemmod, 'C:\TP70\BGI');  {Inicializálás}
      If GraphResult <> 0 then
        begin
          WriteLn('Grafikus hiba!');  {Nem sikerült az inicializálás, kilépés a programból}
          ReadLn;
          Halt
        end;
      ...{Grafika használata}
      CloseGraph    {Grafikus képernyő bezárása}
    end
Színek:
Konstansok: 16 színű üzemmódban megegyeznek a Crt unit konstansaival.
Eljárások: SetColor, SetBkColor, stb.
Függvények: GetColor, GetBkColor, stb.

Rajz:
Típusok: LineSettingsType (ld. GetLineSettings), stb.
Konstansok: vonalstílus (pl. SolidLn, DottedLn, stb. ld. SetLineStyle), vonalvastagság ( NormWidth. ThickWidth ld. ld. SetLineStyle), rajzolási mód (pl. CopyPut, XorPut, stb. ld. SetWritMode)
Eljárások: PutPixel, Line, LineTo, LineRel, Circle, Rectangle,SetLineStyle,GetLineSettings, SetWriteMode, stb.

Kitöltött rajz:
Típusok: FillSettingsType (ld. GetFillSettings), stb.
Konstansok: kitöltési stílus (pl. SolidFill, LineFill, stb. ld. SetFillStyle)
Eljárások: Bar, Bar3D, FillEllipse, FloodFill, SetFillStyle, GetFillSettings, stb.

Szöveg:
Típusok: TextSettingsType (ld. GetTextSettings), stb.
Konstansok: betűtípus, szövegállás, szövegigazítás
Eljárások: OutText, OutTextXY, SetTextStyle, GetTextSettings stb.

Kurzor:
Függvények: GetX, GetY, stb.
Eljárások: MoveTo, MoveRel, stb.

Egyéb:
Kép mentése egy változóba, visszatöltése: ImageSize, GetImage, PutImage.

Példa:
1. Kérjük be grafikus képernyőn egy parabola és egy egyenes egyenletét. Ábrázoljuk a görbéket, és metszéspontjaikat határozzuk meg grafikusan valamint analitikusan. A program tegye lehetővé, hogy a le- ill. felfele nyíllal Y irányban mozgathassuk az egyenest, a jobbra ill. balra nyíllal a meredekségét változtathassuk, a PgUp ill. PgDn billentyűkkel pedig a koordinátarendszer méretarányát (képpont/egység) változtathassuk.
Megoldás


11. Mutatók

11.1 Típusos mutató

Deklarálása: azonosító: ^alaptípus

Pl. Var p1, p2: ^real;

A mutató egy memóriacímet (4 bájtos: szegmens, ofszet) tartalmaz, egy alaptípusú változóra mutat. A mutatóhoz futás közben rendelhetünk memóriacímet és így tárterületet (dinamikus adattárolás).
A New eljárás a heap memóriában foglalterületet a mutatott változó számára, a Dispose eljárás pedig felszabadítja a lefoglalt területet. Így lehetővé válik, hogy egy nagy helyfoglalású adatstruktúra számára csak akkor kössünk le memóriát, amikor használjuk a változót. Nézzünk erre egy példát:

    type TTomb = array[1..1000]of real;
    var t: TTomb;  {A rendszer már a program idításakor lefoglal 6000 bájtot az adatszegmensben}
        pt: ^TTomb;{A rendszer a program idításakor csak 4 bájtot foglal le a mutató számára}
    begin
      ...
      New(pt);     {A heap-ben létrejön a mutatott változó}
      ...          {Használhatom a pt által mutatott változót (pt^)}
      Dispose(pt)  {A terület felszabadul a heap-ben}
    end.
A mutatót ráirányíthatjuk egy az alaptípusával megegyező típusú változóra a @ operátorral vagy az Addr függvénnyel. Pl. pt := @t; vagy pt := Addr(t).
A Ptr függvénnyel a mutatónak egy tetszőleges memóriacímet adhatunk értékül.
A negyedik lehetőség arra, hogy egy mutatóhoz egy memóriacímet rendeljünk: értékadó utasítás egy vele azonos alaptípusú mutatóval.

Hivatkozás a mutatott változóra: mutató-azonosító^
(pl.: pt^[12] := 1)

Mutató típusú konstans: Nil.
A Nil nem mutat sehová. (Pl. láncolt lista végének a jelzésére használhatjuk).

Műveletek:
Címe: @
Egyenlőség vizsgálat: =, <>

További szabványos eljárások, függvények:
Eljárások: Mark, Release
Függvények: MaxAvail, MemAvail, Ofs, Seg

Láncolt listák

A különböző típusú láncolt listák nagyon fontos adatszerkezetek a számítástechnikában. Az egyes adatelemek (rekordok) közötti csatolást mutatókkal valósíthatjuk meg, így a rekordnak van egy mutató típusú mezője, melynek alaptípusa maga a rekord. Az alábbi példában figyeljük meg, hogy a mutató típus deklarálásánál olyan azonosítót használunk, amely a programunkban csak később szerepel. (A Pascal ebben az egy esetben engedi ezt meg.)

    type Mutato = ^Adatelem;
         Adatelem = record
           Adat: real;
           Kovetkezo: Mutato;
         end;
    var Elso, Uj, Aktualis: Mutato;
Példák:
1. Fűzzük fel láncolt listára a billentyűzetről beolvasott számokat. Írassuk ki a listát!
Megoldás
2. Fűzzük fel rendezett láncolt listára a billentyűzetről beolvasott számokat. Írassuk ki a listát!
Megoldás

11.2 Típusnélküli mutató - Pointer

Deklarálása: POINTER

Egy négy bájtos memóriacímet tartalmaz. A mutatott változónak nincs típusa. A típusnélküli műveleteknél használjuk (pl. adatmozgatás).
A GetMem eljárással foglalhatunk le egy megadott méretű területet a heap-ben a mutatónk számára. Ha a memóriaterületre már nincs szükségünk a FreeMem eljárással azt felszabadíthatjuk.
A mutatót ráirányíthatjuk akármilyen címre vagy változóra a @ operátorral vagy az Addr függvénnyel. Pl. p := @tomb1; vagy p := Addr(tomb1).
A Ptr függvénnyel a mutatónak egy tetszőleges memóriacímet adhatunk értékül.
Egy mutató értékadó utasítással egy másik mutató címét is felveheti.
A mutatott változóhoz rendelhetünk típust: típus(mutató-azonosító^).

Hivatkozás a mutatott változóra: mutató-azonosító^

Mutató típusú konstans: Nil.

Műveletek:
Címe: @
Egyenlőség vizsgálat: =, <>

További szabványos függvények: MaxAvail, MemAvail, Ofs, Seg

Példa:
1. Mentsünk el egy garfikát egy típus nélküli állományba, majd olvassuk vissza!
Megoldás


12. Rendszerközeli programozás

12.1 Memóriakezelés

12.1.1 Memória felosztás

Az alábbi ábrán a Turbo Pascal memóriatérképét láthatjuk. A memória logikai egységeinek az elérését a System unit által deklarált változók (PrefixSeg, HeapOrg, HeapPtr, HeapEnd, OvrHeapOrg, OvrHeapEnd) és függvények (Dseg, SSeg, SPtr) segítik.

PSP (Program Segment Prefix)
Az DOS hozza létre a program betöltésekor.

Kódszegmensek
A főprogramnak és minden unitnak van egy kódszegmense. A System egység kódszegmense minden prograhoz automatikusan hozzászerkesztődik. Ezután (fordított sorrendben) következnek a Uses kulcsszó után megadott unitok kódszegmensei. Egy kódszegmens mérete legfeljebb 64 kB lehet. Nagy program írásakor könnyen ütközhetünk ebbe a korlátba. Ekkor főprogramunk méretét csökkenthetjük saját unitok készítésével, melyeknek mérete természetesen külön-külön 64 kB lehet.

Adatszegmens
Programunknak egy adatszegmense van, amely tartalmazza a főprogram és az összes unit globális deklarációit (típusos konstansok, változók). Maximális mérete 64 kB lehet. Ha ez nem elegendő a heap memóriát (ld. mutatók) vagy a verem memóriát (lokális változók) használhatjuk adataink tárolására.

Veremszegmens
LIFO (Last In First Out) szervezésű memória, az utolsónak beírt adatot olvashatom ki belőle először. A renndszer itt tárolja az alprogramok visszatérési címeit, paramétereit, lokális változóit. Méretét a $M fordítási direktívával megadhatjuk, maximálisan 64kB.

Heap
Futás közben a heap-ben foglalhatunk le illetve szabadíthatunk fel memóriát a mutatók számára. Alapértelmezés szerint a lefoglalja az egész maradék memóriát. Méretét a $M fordítási direktívával korlátozhatjuk.

12.1.2 Közvetlen memóriahozzáférés

a, A System unitban definiált tömbök segítségével a memória bármely bájtját, szavát (2 bájt) dupla szavát (4 bájt) kiolvashatjuk illetve felülírhatjuk:
Mem[szegmens:eltolás]
MemW[szegmens:eltolás]
MemL[szegmens:eltolás]

Az alábbi példa a képernyő 5. sorának 10. oszlopába kiír egy szines A betűt (ld CRT unit).

    var sor, oszlop: byte;
    begin
      sor := 5;
      oszlop := 10;
      Mem[$B800:sor*160+oszlop*2] := Ord('A'); {$B800 a képernyőmemória kezdőcíme}
      Mem[$B800:sor*160+oszlop*2+1] := $14     {Kék háttéren piros betű}
    end.
b, Az Absolute direktíva segítségével egy változóhoz közvetlenül rendelhetünk rendelhetünk memóriacímet a deklaráció során. Ez lehet egy abszolút memóriacím (szegmens:eltolás), vagy egy másik változó címe.

Az előző példa egy másik megoldása:

    var kepernyo: array[1..25,1..80,1..2]of byte absolute $B800:0;
    begin
      kepernyo[5, 10, 1] := Ord('A');
      kepernyo[5, 10, 2] := $14
    end.
Az alábbi példaprogram a beolvasott sztring hosszát írja ki:
    uses Crt;
    var s: string;
        b: byte absolute s;
    begin
      ReadLn(s);
      WriteLn(b);  {A sztring 1. (0. indexű) bájtját írja ki, mint egy egész számot}  
    end.

12.1.3 A Seg és az Ofs függvények

A Seg függvény egy változó vagy egy alprogram kezdőcímének a szegmenscímét, míg az Ofs függvény az ofszet (eltolási) címét adja vissza.

Például az alábbi program a d és i integer változók (2-2 bájt) valamint az r rekord (2+2+6 bájt) együttes méretét (azaz 14-et) írja ki bájtokban:

    uses Crt;
    var d: integer;
        r: record
            a, b: integer;
            x: real;
         end;
        i, j: integer;
    begin
      WriteLn(Ofs(j) - Ofs(d));
    end.

12.2 Kapcsolat a DOS-szal, a Dos egység

Már a System unit is tartalmaz olyan eljárásokat és függvényeket, melyek a háttértárolók kezelését segítik. Ezek közül az már jó néhányat megismertünk az Állománykezelés fejezetben.
További eljárások: ChDir, MkDir, RmDir, GetDir

A Dos egység további segítséget nyújt az állománykezeléshez, valamint a rendszerközeli programozáshoz. A teljesség igénye nélkül nézzünk néhány területet:

Állománykezelés

A FindFirst a paramétereiben meghatározott fájlok közül megkeresi az elsőt, és jellemzőit elhelyezi a (unit által definiált) SearchRec típusú paraméterében. A SearchRec deklarációja:

    SearchRec = record
      Fill: array[1..21] of byte;   {A DOS számára foglalt}
      Attr: byte;                   {Az állomány attribútuma}
      Time: longint;                {Létrehozási idő (pakoltan)}
      Size: longint;                {Az állomány mérete}
      Name: string[12];             {Az állomány neve}
    end;
A FindNext az előzőleg FindFirst eljárással definiált fájlok közül a következőt keresi ki. Mint a Dos unit legtöbb eljárása, így ezen két eljárás is a DosError változóban adja vissza az információt a végrehajtás sikerességéről (értéke 0, ha talált állományt).

Megj.: További eljárások: FSplit, GetFAttr, SetFAttr; függvények: FExpand, FSearch.

Példa:
Írjunk programot, amely az aktuális könyvtár pas kiterjesztésű állományaiban keres egy szót. A program írja ki, hogy mely állományok és ott mely sorokban fordul elő a keresett szó!
Megoldás

A rendszerdátum és idő kezelése

A GetDate és a GetTime eljárásokkal az aktuális dátumot és időt kérdezhetjük le, míg a SetDate és SetTime eljárásokkal beállíthatjuk azokat. Például az aktuális idő kiírása:

    uses Dos;
    var ora, perc, mperc, szazadmp: word;
    begin
      GetTime(ora, perc, mperc, szazadmp);
      WriteLn(ora,':',perc,':',mperc)
    end.
Megj.: A GetFTime, SetFTime, PackTime, UnPackTime eljárások egy állomány utolsó módosításának az idejét kezelik. Ezen eljárások használják a unit által definiált DateTime típust.

Megszakítások kezelése

Az Intr eljárás meghívja a paramétereként megadott megszakítást. A megszakítás működését a mikroprocesszor regisztereinek a beállításával szabályozhatjuk, valamint a megszakítás eredményét a regiszterekben kapjuk vissza. A regiszterekhez való hozzáférést a unit által deklarált Registers típus segíti. Az alábbi programocska kiír 10 piros csillagot kék háttéren a képernyőre a képernyőkezelő $10 (hexadecimális 10) BIOS megszakítás segítségével:

    uses Dos;
    var r: registers;
      begin
      r.ah := $9;        {A megszakítás $9 funkciója (karakterek megjelenítése)}
      r.al := ord('*');  {A karakter kódja} 
      r.bl := $14;       {A karakter attribútuma (szín, háttérszín)}
      r.cx := 10;        {Az ismétlések száma}
      Intr($10, r)       {A 10h megszakítás hívása a fent beállított regiszterekkel}
    end.
Megj.: Az MsDos eljárás a Dos függvényeket megvalósító $21 megszakítást hívja meg. A GetIntVec illetve SetIntVec eljárással egy megszakításvektort kérdezhetünk le illetve állíthatunk be.

Külső program indítása

Az Exec eljárással futtathatunk egy külső programot. A {$M} fordítási direktívával a heap felső határát le kell csökkenteni úgy, hogy a külső program beférjen a memóriába. Az eljárás előtt és után hívjuk meg a SwapVectors eljárást. Például:

    {$M 16000, 0, 0}     {A verem, a heap minimális és a heap maximális méretének beállítása}
    uses Crt, Dos;
    begin
      ClrScr;
      SwapVectors;
      Exec('c:\command.com', '/c dir *.pas'); {command.com esetén az első parancssor paraméter}  
      SwapVectors;                            {/c kell hogy legyen}  
      ReadKey;
      SwapVectors;
      Exec('c:\vc\vc.com', '');
      SwapVectors;
    end.

12.3 Az egér programozása

A BIOS $33 megszakítása tartalmazza az egétkezelő rutinokat. Ezen rutinok bemeneteit és kimeneteit a mikroporcesszor regiszterei alkotják, melyeket a DOS unit Registers típusának segítségével érhetünk el. A megszakítás egyes funkcióit az AX regiszter béállításával érhetjük el. (Akár az első négy funkcióval már kényelmes egérkezelés valósítható meg.)
Az egyes rutinok leírása.

Célszerű az egyes egérkezelési teendőkre eljárásokat és függvényeket írni (akár egy külön unit-ban elhelyezni), majd ezeket használni a programban. Ezt láthatjuk az alábbi példában is.

Példa:
Rajzolhassunk az egérrel (balgomb), a jobboldali gomb lenyomása után téglalapokat rajzolhassunk, újabb jobbgomb, kilépés a programból.
Megoldás


13. Saját unit készítése

Az egységek (unitok) előre lefordított programmodulok. Általában egy adott területhez tartozó eljárásokat, függvényeket tartalmaznak, illetve deklarálják az általuk használt konstansokat, típusokat, változókat. Mivel a kódszegmens maximálisan 64 kB lehet, így programunk nagysága is korlátozott. Ha elértük a határt (kb. 2-3000 programsor), akkor programunk egyes részeit saját unitokban helyezhetjük el, melyek külön-külön szintén 64 kB méretűek lehetnek.

Az egység felépítése:

Egységfej:

Unit azonosító;

Az azonosítót kell megadnunk a Uses kulcsszó után abban a programban vagy egységben, ahol a unitot használni szeretnénk, továbbá az azonosító legyen a neve az elmentett forrásnyelvű unitnak.

Illesztő rész:

INTERFACE
[USES azonosító [,azonosító...];]

Továbbá globális deklarációk (konstansok, típusok, címkék, változók, eljárások, függvények), melyeket az egységet használó programokban illetve egységekben is elérhetünk. Az eljárásoknak, függvényeknek itt csak a fejlécei szerepelnek.

Kifejtő rész:

IMPLEMENTATION
[USES azonosító [,azonosító...];]

Továbbá egység hatáskörű deklarációk (konstansok, típusok, címkék, változók, eljárások, függvények), melyeket csak ebben az egységben érhetünk el. Itt fejtjük ki az Interface részben deklarált eljárásokat, függvényeket is.

Inicializáló rész:

[BEGIN
[utasítás [; utasítás...]]
END.

A főprogram első utasítása előtt egyszer végrehajtódik, elhagyható.

Ha több egység egymást kölcsönösen használja, akkor mindegyikben a többi egység nevét az implementációs rész Uses kulcsszava után kell megadni.

Ha az egységet elkészítettük, .pas kiterjesztéssel metjük lemezre. Az egységet le kell fordítanunk (a fordítást lemezre kérjük). A lefordított egység kiterjesztése .tpu lesz.

Példa:
Az egérkezelést megvalósító rutinokat lehelyezhetjük egy unitban.
Megoldás