poppe-kocsis - c programozási feladatgyűjtemény (1992, 80 oldal)

80
C programoz´ asi feladatgy˝ ujtem´ eny Poppe Andr´ as – Kocsis Tam´ as BME M´ ern¨ oki Tov´ abbk´ epz˝ o Int´ ezet, 1992.

Upload: kingbela

Post on 24-Jun-2015

259 views

Category:

Documents


3 download

TRANSCRIPT

Page 1: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

C programozasifeladatgyujtemeny

Poppe Andras – Kocsis Tamas

BME Mernoki Tovabbkepzo Intezet, 1992.

Page 2: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

Tartalomjegyzek

Eloszo iii

1. C alapok 11.1. Ciklusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1

1.1.1. Feladat: Fahrenheit-Celsius atszamolo program . . . 11.1.2. Feladat: Fahrenheit-Celsius atszamolo for ciklussal 3

1.2. Egykarakteres I/O . . . . . . . . . . . . . . . . . . . . . . . 51.2.1. Feladat: Karakterszamlas . . . . . . . . . . . . . . . 6

1.3. Elagazas: if, switch . . . . . . . . . . . . . . . . . . . . . . 71.3.1. Feladat: Sorszamlalas . . . . . . . . . . . . . . . . . 7

1.4. Onallo feladatok . . . . . . . . . . . . . . . . . . . . . . . . 81.4.1. Feladat: Blank-ek szamlalasa . . . . . . . . . . . . . 81.4.2. Feladat: Blank-ek szamlalasa tıpusonkent . . . . . . 8

1.5. Az operatorok gyakorlasa . . . . . . . . . . . . . . . . . . . 81.5.1. Feladat: A sizeof egyszeru hasznalta . . . . . . . . 81.5.2. Feladat: A sizeof es 2-vel valo szorzas a shiftelessel 81.5.3. Feladatok: Aritmetikai muveletek int-ekkel . . . . . 8

2. Bonyolultabb szerkezetek 112.1. Az elofeldolgozo hasznalata . . . . . . . . . . . . . . . . . . 11

2.1.1. Szimbolumok hasznalata, felteteles fordıtas . . . . . 122.1.2. Feladat: Programfordıtas korulmenyeinek kiiratasa . 132.1.3. Feladat: Az #ifdef alkalmazasa . . . . . . . . . . . 142.1.4. Fordıtasi idoben elo szimbolumok . . . . . . . . . . . 152.1.5. Uj es regi stılusu fuggvenydeklaracok . . . . . . . . . 152.1.6. Feladat: . . . . . . . . . . . . . . . . . . . . . . . . . 172.1.7. Makrodefiniciok . . . . . . . . . . . . . . . . . . . . . 172.1.8. Feladatok: min, max, pow, toupper, tolower . . . . 18

2.2. Tomb-, pointer- es fuggvenytıpusok . . . . . . . . . . . . . . 192.3. Karaktertombok es pointerek . . . . . . . . . . . . . . . . . 23

i

Page 3: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

ii

2.3.1. Feladat: sajat strcpy . . . . . . . . . . . . . . . . . 232.3.2. Feladat: sajat strlen . . . . . . . . . . . . . . . . . 25

2.4. Fuggvenypointerek . . . . . . . . . . . . . . . . . . . . . . . 252.5. Tobb modulbol allo programok keszıtese . . . . . . . . . . . 30

3. A dinamikus tarkezeles alapjai 333.1. Dinamikus adatok . . . . . . . . . . . . . . . . . . . . . . . 33

3.1.1. Feladat: linearis egyenletrendszer megoldasa . . . . . 35

4. Az operacios rendszerrel valo kapcsolat 394.1. Folyam jellegu I/O . . . . . . . . . . . . . . . . . . . . . . . 39

4.1.1. Feladatok: File-ok masolasa . . . . . . . . . . . . . . 404.1.2. File-nyitasi hibak, azok kezelese . . . . . . . . . . . . 40

4.2. A main argumentumai . . . . . . . . . . . . . . . . . . . . . 414.2.1. Feladat: A copy parancs – sajat kivitelben . . . . . 41

5. Fejlettebb technikak 435.1. Strukturak – lancolt lista . . . . . . . . . . . . . . . . . . . 43

5.1.1. Fealadat: Lancolt lista keszıtese . . . . . . . . . . . 435.2. Menurendszer . . . . . . . . . . . . . . . . . . . . . . . . . . 465.3. Osszetett mintapelda . . . . . . . . . . . . . . . . . . . . . . 47

5.3.1. A tervezes egyes fazisai . . . . . . . . . . . . . . . . 475.3.2. A menukezelo rendszer listaja . . . . . . . . . . . . . 55

Irodalomjegyzek 75

Page 4: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

Eloszo

A C programozasi nyelv az egyik legnepszerubb programfejlesztesi eszkoza vilagon. Ez nagy reszt a mikro- es miniszamıtogepek, illetve az un.munkaallomasok (worktstation-ok) vilagmeretu ternyeresenek koszonheto.Ezen gepkategoriak Magyarorszagon, illetve szukebben a Budapesti Musza-ki Egyetemen elterjedt reprezentansain is (IBM PC/AT, VAX/MicroVAX,SUN, stb.) hatekony C fejlesztoi kornyezeteket talalhatunk.

A programfejlesztok szıvesen dolgoznak a C-vel, mert altalanos celu, al-kalmas igen nagy lelegzetu csoportmunkakban valo felhasznalasra, nagyonjo hatasfoku kodot lehet vele eloallıtani, megis magas szinten strukturalt,atfogoan szabvanyosıtott nyelv. Ez utobbi azt jelenti, hogy egy adott gep-tıpus adott operacios rendszerere kidolgozott – bizonyos programırasi sza-balyokat figyelembe vevo – program viszonylag kis munkaval, jol megha-tarozott helyeken valo modosıtassal atırhato mas szamıtogep tetszoleges (aC nyelvet tamogato) operacios rendszere ala. Itt azonban rogton meg kelljegyeznunk azt is, hogy nagyon konnyu C nyelven attekinthetetlen, nehezenmegertheto es modosıthato programokat keszıteni. Nagyon fontos tehat afegyelmezett, korultekinto programozasi stılus alkalmazasa, aminek az elsa-jatıtasa kb. annyi munkat igenyelhet, mint maguknak a nyelvi elemekneka megtanulasa.

Tekintve, hogy a C nepszerusege no, egyre tobben szeretnek a nyelvet el-sajatıtani. Ehhez egyre tobb, a C nyelvet ismerteto konyv all rendelkezesre,de ezek kozt keves tartalmaz olyan mintaprogramot, illetve mintafeladatot,amelyek segıtenek elmelyıteni a C nyelv ismertet. Feladatgyujtemenyunk– legalabbis ugy hisszuk – ezt a hianyt igyekszik potolni olymodon, hogyegyes C nyelvi elemekhez kapcsolodva mintaprogramokat, illetve program-reszleteket kozol, illetve feladatkituzeseket tartalmaz.

A hianypotlason tul, masik celunk az, hogy segıtseget nyujtsunk egytiszta, a nyelvi elemeket jol kihasznalo, portabilis C programozasi stılus el-sajatıtasahoz. Azt is igyekszunk bemutatni – egy, a BME VillamosmernokiKaranak nappali tagozatan szokasos programozasi nagy hazi feladat megol-dasanak ismertetesevel – hogy milyen az un. ondokumentalo program, egy

iii

Page 5: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

iv Eloszo

kesz, nagyobb lelegzetu C programot hogy dokumentaljunk, ehhez milyensegedprogramokat vehetunk igenybe.

A szovegben az IBM PC kompatibilis gepeken szeles korben elterjedtTURBO/BORLAND C(++) fordıtokkal, illetve a VAX tıpusu szamıto-gepek VMS operacios rendszere alatt elerheto VAX-C fordıtoval egyarantlefordıthato programpeldakat szerepeltetunk.

Feltetelezzuk, hogy e feladatgyujtemenyt forgato olvasoknak valamilyenmas programozasi nyelv – peldaul a Pascal – hasznalataban mar van nemirutinjuk. A C nyelvet termeszetesen egy peldatarbol nem lehet megta-nulni, ıgy javasoljuk, hogy a mintaprogramok, illetve a kituzott feladatokfeldolgozasat egy, a C nyelvet ismerteto konyv tanulmanyozasaval parhu-zamosan vegezze az Olvaso. Feladatgyujtemenyunk anyaganak felepıteseolyan, hogy tobbe-kevesbe koveti a BME Mernoki Tovabbkepzo Intezetealtal kiadott Az IBM PC programozasa Turbo C 2.0 nyelven c. jegyzet [1],illetve a ComputerBooks kiadonal 1991-ben megjelent Bevezetes a BOR-LAND C++ programozasba c. konyv [2] anyagat. A peldatar szovegeben– ajaanlott kiegeszıto olvasmanykent – ez utobbi munka fejezetszamairafogunk hivatkozni.

Haszonnal forgathatja az olvaso Kerninghan es Ritchie A C programo-zasi nyelv cımu konyvet [3], illetve e konyv masodik, csak angol nyelvenhozzaferheto kiadasat [4] is: bizonyos feladatkituzeseket onnan vettunk at.

Az egyes C nyelvi implementaciokra vonatkozo reszletes ismeretekrenincs szukseg e feladatgyujtemeny hasznalata soran, mindazonaltal celszerulehet ezek forgatasa komolyabb programfejlesztesi munkaknal.

Koszonetnyilvanıtas

Szeretnenk megkoszonni Benko Tibornenak azt, hogy faradhatatlanul bız-tatott minket arra, hogy a BME Villamosmernoki Karanak nappali tago-zatos 1. evfolyamos hallgatoinak oktatasa soran osszegyujtott tapasztala-tainkat jelen peldatar osszeallıtasaval kozze tegyuk.

Koszonet illeti Verhas Petert is, aki HION nevu programjat rendelkeze-sunkre bocsatotta. Ez a program tette lehetove, hogy a magyar nyelvu,ekezetes szovegfile-jainkat gond nelkul hasznalhassuk a TEXszovegformazorendszer LATEXmakrocsomagjaval.

Budapest, 1998. szeptember 6. A szerzok

Page 6: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

1. fejezet

C alapok

Jelen fejezet celja az, hogy megereztesse a C programozas ızet. Mivel a tel-jesen portabilis programozasi stılust igyekszunk bemutatni, az itt ismertettpeldak, illetve a kituzott feladatok akar TURBO C-vel, akar BORLANDC++-szal, akar VAX C-vel lefordıthatok. Lehetoseg szerint ANSI C fordı-tasi opciot hasznaljunk, ha IBM PC-n dolgozunk.

A tovabbiakban feltetelezzuk, hogy az olvaso alaposan ismer mar egyprogramozasi nyelvet, peldaul a Pascal-t. Eloszor egyszeru peldakat koz-lunk mind Pascal, mind C nyelven – reszben csak egyszeru szintaxis val-tassal, reszben kihasznalva a C nyujtotta tomorıtesi lehetosegeket, majdegyes problemaknak a C nyelvu megvalosıtasat kozoljuk, vegul pedig csakfeladatkiırasokat adunk meg, a C nyelvet tanulokra bızva az egyes felada-tok konkret, C nyelvu megvalosıtasat. E fejezet feldolgozasahoz javasoltolvasmany [2]-bol: 2.1, 2.2, 2.4.4, 2.4.5, 2.5, 2.7 fejezetek.

1.1. Ciklusok

1.1.1. Feladat: Fahrenheit-Celsius atszamolo program

Keszıtsunk olyan programot, amely egy adott tartomanyon belul, adottlepeskozzel kilistazza a Fahrenheit fokokban adott homerseklet Celsius fo-kokban szamolt erteket.

Ezzel a mintaprogrammal kezdodik Kerninghan es Ritchie konyve is.Mi eloszor a C-ben meg jaratlan olvaso kedveert Pascal nyelven kozoljuka megoldast, majd megadjuk ugyanezt a programot C-ben is. A C-re va-lo atterest egyszeru szintaxis-valtassal oldottuk meg. Igy a program egy

1

Page 7: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2 1. FEJEZET. C ALAPOK

C fordıtoval mar lefordıthato, de meg nem ”C nyelvu”. Ezalatt azt ert-juk, hogy egyaltalan nem hasznalja ki a C lehetosegeit. A C-ben kodoltprogramvaltozat fokozatos finomıtasaval jutunk el egy mar C programnaknevezheto valtozathoz.

A Pascal valtozat:

PROGRAM FAHRANHEIT(INPUT, OUTPUT);VAR FAHR, CELS: INTEGER;

LOWER, UPPER, STEP: INTEGER;

BEGINLOWER:=0;UPPER:=300;STEP:=20;

FAHR:=LOWER;WRITELN;WHILE(FAHR <= UPPER) DOBEGIN

CELS:=5*(FAHR-32) DIV 9;WRITELN(FAHR,’ ’,CELS);FAHR:=FAHR+STEP;

END;END.

A C valtozat:

#include <stdio.h>main(){

int fahr, cels;int lower, upper, step;

lower = 0;upper = 300;step = 20;

fahr = lower;printf("\n");while(fahr <= upper){

Page 8: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

1.1. CIKLUSOK 3

cels = 5 * (fahr - 32) / 8;printf("%d\t%d\n",fahr,cels);fahr = fahr + step;

}}

1.1.2. Feladat: Fahrenheit-Celsius atszamolo for cik-lussal

Alakıtsuk at a C valtozatot ugy, hogy a while ciklus helyett for ciklussalmukodjon! (Lasd [2]-bol a 2.7.3-as reszt.)

Gondoljuk vegig, hogy mi az inicializalo resz, amit lehet, azt a vesz-szo operator segıtsegevel rakjuk a for ciklus inicializalo kifejezes reszebe!Hasznaljuk ki a C nyujtotta tomorıtesi lehetosegeket!

Megoldas:

#include <stdio.h>main(){

int fahr, cels;int lower, upper, step;

printf("\n");for (fhar = lower = 0, upper = 300, step = 20;

fahr <= upper;fahr += step)

{cels = 5 * (fahr- 32) / 8;printf("%d\t%d\n",fahr,cels);

}}

Tovabbi lehetoseg:

Az inicializalast athelyezzuk a deklaracios reszbe, azaz inicializalt valtozo-kat hozunk letre. Ekkor a for ciklus inicializalo resze egy ures utasıtaslesz:

#include <stdio.h>main(){

int fahr = 0, cels;

Page 9: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

4 1. FEJEZET. C ALAPOK

int lower = 0, upper = 300, step = 20;

printf("\n");for ( ; /* initialization - empty statement */

fahr <= upper; /* condition */fahr += step) /* stepping */

{cels = 5 * (fahr- 32) / 8;printf("%d\t%d\n",fahr,cels);

}}

Szimbolumok hasznalata:

Hasznaljunk szimbolikus konstansokat a konkret numerikus ertekek helyetta foprogramon belul! (Lasd [2] 2.3-as, az un. elofeldolgozorol szolo fejeze-tent, azon belul a 2.3.1-es szakaszt.) Az elozo programvaltozat szimbolikuskonstansok felhasznalasaval tehat ıgy nez ki:

#include <stdio.h>

#define LOWER 0#define UPPER 300#define STEP 20main(){

int fahr = LOWER, cels;int lower = LOWER, upper = UPPER, step = STEP;

printf("\n");for ( ; /* initialization - empty statement */

fahr <= upper; /* condition */fahr += step) /* stepping */

{cels = 5 * (fahr- 32) / 8;printf("%d\t%d\n",fahr,cels);

}}

Page 10: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

1.2. EGYKARAKTERES I/O 5

1.2. Egykarakteres I/O

Gyakoroljuk a szabvanyos input/output egykarakteres kezeleset! Ehhez azstdio.h konyvtar getchar (egy karakter beolvasasa a billentyuzetrol) esputchar (egy karakter nyomtatasa a kepernyore) rutinjait hasznaljuk fel.(Lasd [2] 2.10.2 fejezetenek elejet!) Hasznalni fogjuk az EOF (end-of-file)szimbolumot is, amely ugyancsak az stdio.h-ban van definialva. Indul-junk ki az alabbi egyszeru Pascal programbol:

PROGRAM CHARCOPY(INPUT, OUTPUT);VAR CH: CHAR;BEGIN

WHILE NOT EOF DOBEGIN

READ(CH);WRITE(CH);

END;END.

Ennek C megfeleloje:

#include <stdio.h>

main(){

int ch; /* int and char are compatible */

ch = getchar();while (ch != EOF){

putchar(ch);ch = getchar();

}}

Megjegyzes: Mivel a VAX gepeken a VMS operacios rendszer alatt un.bufferelt, echozott I/O van, ezert egeszen addig, amig az elso RETURN-tnem utjuk le a terminal billentyuzeten, gyulnek a karakterek (es ki is ırod-nak a kepernyore), a VMS csak ezutan adja at az input buffer tartalmat aszabvanyos bemenetet olvaso rutinnak (a Pascal READ-nek, illetve a C get-char-nak). A file-veget a a PC-ken a CTRL-Z jelenti. A VAX CTRL-Z-tmeg beolvasott karakternek tekinti, de az EXIT uzenet utan beall az EOFallapot, ıgy a program leall.

Page 11: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

6 1. FEJEZET. C ALAPOK

Tovabbfejlesztes:

Hasznaljuk ki, hogy a C ertekado operator mellekhatasa az, hogy a balertekmint kesz kifejezes azonnal felhasznalhato. Ilyenforman a while ciklus logi-kai kifejezeset ado relacios muvelet baloldalan allo ch valtozonak magabana while-feltetel kifejezesben adhatunk erteket. A muveletek helyes kierteke-lesi sorrendjet azzal biztosıtjuk, hogy az ertekado muveletet zarojelek kozetesszuk. (A zarojel-par is egy operator; hatasa az, hogy az operandusatazonnal kiertekeli.)

#include <stdio.h>

main(){

int ch; /* int and char are compatible */

while ((ch = getchar()) != EOF){

putchar(ch);}

}

1.2.1. Feladat: Karakterszamlas

Az elobbi pelda alapjan ırjunk olyan C programot, amely megszamlalja,hany karaktert olvastunk be a szabvanyos bemeneti allomanyrol! Annyimodosıtasra van szukseg, hogy a ciklustorzsben nem iratjuk ki a beolvasottkaraktert, hanem csak egy szamlalo erteket noveljuk. Ime a program:

#include <stdio.h>

main(){

int n = 0;

while (getchar() != EOF){

n = n + 1; /* or n += 1; or n++; */}printf("\nNumber of characters read from stdin: %d\n",n);

}

Page 12: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

1.3. ELAGAZAS: IF, SWITCH 7

Amint azt a programbeli megjegyzesbol is lathatjuk, az igencsak Pascalosn = n + 1; utasıtast sokkal tomerebben is leırhatjuk C-ben az un. post-increment operator felhasznalasaval: n++;

1.3. Elagazas: if, switch

1.3.1. Feladat: Sorszamlalas

Elso lepesben bovıtsuk ki ugy az elobbi, karakterszamlalo programot, hogyazt is szamlalja, hany sorbol allt az input. Ehhez pusztan a beolvasott ’\n’karaktereket kell kulon szamlalni:

#include <stdio.h>

main(){

int n, nl;int ch;

n = nl = 0;while ((ch = getchar()) != EOF){

n++if (ch == ’\n’) nl++;

}printf("\nNumber of characters read from stdin: %d\n",n);printf("Number of lines read from stdin: %d\n",nl);

}

Most tomorıtsunk egy kicsit ezen a programon! Kihasznalva azt, hogy arelacios operatorok ligikai erteket szolgaltatnak, ami vagy 1 (ha igaz a re-lacio), vagy 0 (ha nem igaz), a kovetkezokeppen ırhatjuk at programunkat:

#include <stdio.h>

main(){

int n, nl;int ch;

n = nl = 0;while ((ch = getchar()) != EOF){

Page 13: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

8 1. FEJEZET. C ALAPOK

n++nl += (ch == ’\n’);

}printf("\nNumber of characters read from stdin: %d\n",n);printf("Number of lines read from stdin: %d\n",nl);

}

1.4. Onallo feladatok

1.4.1. Feladat: Blank-ek szamlalasa

Irjunk onalloan olyan programot, amely azt szamlalja meg, hogy egyutt-veve hany un. blank karakter, azaz szokoz, tabulator (’\t’) vagy ujsorkarakter (’\n’) jon be az inputrol!(Az if utasıtast es a logikai VAGY muveletet felhasznalva.)

1.4.2. Feladat: Blank-ek szamlalasa tıpusonkent

Irjunk onalloan olyan programot, amely megszamlalja, hogy hany szokoz,tabulator (’\t’) es ujsor karakter (’\n’) jon be az inputrol! Hasznaljuk aswitch utasıtast!

1.5. Az operatorok gyakorlasa

1.5.1. Feladat: A sizeof egyszeru hasznalta

Irjunk olyan programot, amely a sizeof operator segıtsegevel meghataroz-za es a kepernyore kiırja az alap-adatıpusok meretet!

1.5.2. Feladat: A sizeof es 2-vel valo szorzas a shifte-lessel

Irjunk olyan programot, amely a blara shifteles operatoranak a segıtsegevelkiırja a kepernyore 2 minden olyan hatvanyat, amely elfer egy int-ben.

1.5.3. Feladatok: Aritmetikai muveletek int-ekkel

Gyakoroljuk az aritmetikai operatorok (osszeadas, kivonas, szorzas, osztas,maradekkepzes) hasznalatat int tıpusu adatok eseten. Irjunk olyan prog-ramot, amely

a) eloallıtja egy egesz szam osszes osztojat,

Page 14: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

1.5. AZ OPERATOROK GYAKORLASA 9

b) eloallıtja egy egesz szam prımtenyezos felbontasat,

c) eldonti egy egesz szamrol, hogy tokeletes szam-e (Egy tokeletes szameggyel nagyobb, mint az osszes valodi osztojanak az osszege.),

d) megadja ket egesz szam legkisebb kozos tobbszoroset.

Page 15: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

10 1. FEJEZET. C ALAPOK

Page 16: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2. fejezet

Bonyolultabb szerkezetek

Az elozo fejezet vegere az alaputasıtasok hasznalata soran sikerult eltavo-lodnunk a Pascal-szeru megoldasoktol. Most sorra vesszuk azokat a lehe-tosegeket, amelyek a C-t igazan hatekony programozasi nyelvve teszik.

Eloszor attekintjuk az elofeldolgozo hasznalatat, ennek kapcsan utalunkarra, hogy keszıthetok portabilis programok a felteteles fordıtasi direktıvakfelhasznalasaval. Ezt kovetoen azzal foglakozunk, hogy ırhatunk un. mak-rokat.

E fejezet folytatasakeppen attekintjuk azt, hogy a C alaptıpusaibol hogyszarmaztathatunk tovabbi tıpusokat, attekintjuk a mutatok es a tombokkapcsolatat. A fuggvenypointerek hasznalatat a qsort fuggveny peldajankeresztul mutatjuk be.

Szinten a fuggvenypointerek kapcsan egy flexibilis numerikus integralofuggvenyt ismertetunk. Ebbol, es nehany integralando fuggvenybol, vala-mint egy main fuggvenybol elkeszıtunk egy tobb forrasmodulbol allo prog-ramot.

2.1. Az elofeldolgozo hasznalata

Az elofeldolgozo egy sororientalt szovegfeldolgozo (mas szoval makronyelv),ami semmit sem ”tud” a C nyelvrol. Ez ket fontos kovetkezmennyel jar: azelofeldolgozonak szolo utasıtasokat nem ırhatjuk olyan kotetlen formaban,mint az egyeb C utasıtasokat (tehat egy sorba csak egy utasıtas kerulhetes a parancsok nem loghatnak at masik sorba, hacsak nem jeloljuk ki foly-tatosornak); masreszt minden, amit az elofeldolgozo muvel, szigoruan csakszovegmanipulacio, fuggetlenul attol, hogy C nyelvi alapszavakon, kifejeze-seken vagy valtozokon dolgozik. Az elofeldolgozo es az un. belso fordıto

11

Page 17: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

12 2. FEJEZET. BONYOLULTABB SZERKEZETEK

forrasszoveg elofeldolgozo belso fordıto targykod- - -

C fordıtoprogram

2.1. abra. Az elofeldolgozo kapcsolata a kornyezettel

Szimbolum Ertelmezes, ertekSTDC 1 ertekkel definialva van, ha ANSI C (egesz)FILE a feldolgozas alatt allo file neve (sztring)LINE a feldolgozas alatt allo sor szama (egesz)DATE a fordıtas datuma (sztring)TIME a fordıtas ideje (sztring)

2.1. tablazat. Elore definialt szabvanyos szimbolumok az ANSI C-ben

kapcsolatat szemlelteti a 2.1 abra.A preprocesszornak szolo parancsokat a sor elejen (esetleg szokozok

es/vagy tabulatorok utan) allo # karakter jelzi. A legfontosabb utasıta-sok: #define, #undef, #include, #if, #ifdef, #else, #elif, #endif.

2.1.1. Szimbolumok hasznalata, felteteles fordıtas

Az elozo fejezetben mar lattuk, hogy a #define direktıva segıtsegevel hogy”nevezhetjuk el” szamkonstansainkat. Termeszetesen a #define-nal let-rehoztt szimbolumokat nemcsak konstans kifejezesek elnevezesere hasznal-hatjuk, hanem peldaul felteteles fordıtasvezerlesre. Ezaltal egyes program-reszletek lefordıtasat kikapcsolhatjuk, illetve bekapcsolhatjuk.

A felteteles fordıtasi direktıvak hasznalata elsosorban a tobb, kulonbozooperacios rendszer alatti fordıtasra szant programokra jellemzo. Altalabanminden C nyelvi rendszer elore definial egyes szimbolumokat, ıgy peldaulolyanokat, amelyek meglete vagy hianya alapjan eldontheto, hogy milyenoperacios rendszer alatt tortenik a fordıtas.

Maga az ANSI C szabvany is elore definial egyes szimbolumokat, ame-lyek alapjan a program fordıtas korulmenyeirol szerezhetunk informaciokat.Ezeket a szimbolumokat az 2.1. tablazatban soroljuk fel.

Bar a legujabb VAX C sokmindenben megfelel az ANSI C-nek, megsincs

Page 18: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2.1. AZ ELOFELDOLGOZO HASZNALATA 13

Szimbolum Ertelmezes, ertekVAX vagy vax A VAX C-ben 1 ertekkel definialva vanVMS vagy vms A VAX C-ben 1 ertekkel definialva vanVAXC vagy vaxc A VAX C-ben 1 ertekkel definialva vanVAX11C vagy vax11c A VAX C-ben 1 ertekkel definialva vanTURBOC Minden Turbo/Borland C-ben definialva vanMSDOS Altalaban minden PC-s C-ben definialva van.BORLANDC A Borland C++-ban mindig definialtva van.

A verzioszamra utal.TCPLUSPLUS Csak C++ uzemmodban van definialva, ekkor a

verzioszamot adja.cplusplus C++ uzemmodban 1 ertekkel van definialva,

egyebkent definialatlan.

2.2. tablazat.A Borland C++-ban es a VAX C-ben definialt, az operacios rendszerre,

illetve a fordıtora utalo szimbolumok

elore definialva a STDC szimbolum, jollehet, az 2.1. tablazatban szereplotobbi szimbolum letezik benne.

A Borland C++-ban a STDC szimbolum akkor van definialva, ha azOptions menuben ANSI C kompatibilisre allıtottuk be a fordıtot.

Az ANSI C altal megadott eloredefinialt szimbolumokon kıvul – mintmar emlıtettuk – minden nyelvi rendszerben vannak olyan eloredefinialtszimbolumok, amelyek az adott nyelvi rendszert, es az operacios rendszertazonosıtjak. A VAX C-ben, illetve a Borland C++-ban elofordulo ilyenszimbolumokat a 2.2. tablazatban foglatuk ossze.

2.1.2. Feladat: Programfordıtas korulmenyeinek kiira-tasa

A elozoek alapjan ırjunk olyan programot, amely abban az esetben, ha AN-SI C, vagy VAXC kompatibilis fordıtoprogrammal fordıtottak le, kiırja aszabvanyos kimenetre, hogy

• milyen nevu forrasallomanybol fordıtottak,

• mikor (datum, ido) tortent a fordıtas,

• kiırja, hogy a main fuggveny hany sorbol all.

Page 19: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

14 2. FEJEZET. BONYOLULTABB SZERKEZETEK

A megoldas:

/***** File: test.c ******/#include <stdio.h>int sor = 0;main(){#if defined(__STDC__) || VAXC

sor = __LINE__;

printf("The compilation circumstances of %s:\n",__FILE__);printf("Date of compilation: %s\n",__DATE__);printf("Time of compilation: %s\n",__TIME__);printf("Approximate length of ’main’: %d\n",

__LINE__ - sor);#else

printf("The compiler is not ANSI C/VAX-C compatible\n");printf("No way to identify compilation circumstances\n");

#endif}

Az elobbi programot egy PC-n, a Borland C++-szal ANSI C uzemmodbanlefordıtva, majd az object modulbol szerkesztett .exe programot lefuttatvaa kovetkezo outputot kapjuk a kepernyon:

The compilation circumstances of TEST.C:Date of compilation: Mar 21 1992Time of compilation: 11:30:35Approximate length of ’main’: 6

A fenti pelda kapcsan bemutatott leheteosegek jelentosege abban rejlik,hogy egy mar kesz, a felhasznalok szamara csak exe file formajaban rendel-kezesre allo programban is elhelyezhetunk olyan teszt-reszleteket, amelyeksegıtsegevel peldaul a felhasznalo egy reszletes hibajelentest kuldhet a prog-ram fejlesztoinek.

2.1.3. Feladat: Az #ifdef alkalmazasa

Egeszıtsuk ki az elobbi test.c programot ugy, hogy attol fuggoen, hogyPC-n a DOS alatt, vagy egy VAX-on, a VMS operacios rendszer allat for-dıtjak le, mas-mas azonosıto szoveget ırjon ki.

Page 20: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2.1. AZ ELOFELDOLGOZO HASZNALATA 15

2.1.4. Fordıtasi idoben elo szimbolumok

Termeszetesen nem csak az adott C nyelvi rendszer altal elore definialt szim-bolumok megletet vizsgalhatjuk az #ifdef preprocesszor utasıtassal, hanemmi magunk is letrehozhatunk szimbolumokat a fordıtas idejere. Ezeket nemkell a forrasprogramjainkban elhelyezni, hanem fordıtasi idoben, a fordıtoprogram szamara parameterkent adhatjuk meg.

A BORLAND C++ integralt kornyezeteben az Options | Compiler |Code genaration dialogus doboz Defines mezojebe ırt sztringre, illetvea VAX C eseteben a CC parancs /DEFINE= kapcsoloja utan ırt sztringrehivatkozva a C forrasprogramban, fordıtaskor ugy talaljuk, hogy az adottszimbolum definialva van. Tekintsuk a kovetkezo peldat:

/***** File: testsymb.c *****/#include <stdio.h>main(){#ifdef MYSYMBOL

printf("MYSYMBOL has been defined for %s\n",__FILE__);#else

printf("MYSYMBOL has not been defined for %s\n",__FILE__);#endif}

Ha tehat a fenti programot egy VAX gepen, VMS-ben a CC TESTSYMB /DE-FINE=MYSYMBOL paranccsal fordıtjuk le, akkor MYSYMBOL has been defi-ned for TESTSYMB.C uzenetet fogja kiırni a kepernyore a futtathato prog-ram, mıg az egyszeru CC TESTSYMB fordıtasi paranccsal fordıtva, a futtat-hato program a masodik uzenetet ırja majd ki. Hasonlo kiserlet vegezhetoPC-ken a MYSYMBOL szimbolum fordıtasi idore torteno definialasaval (azOptions | Compiler | Code genaration | Defines mezo kitoltesevel)

2.1.5. Uj es regi stılusu fuggvenydeklaracok

Az ANSI C szabvany szerint minden fuggveny deklaraciojakor nemcsak avisszateresi tıpust kell megadnunk, (ha nem tesszuk, akkor definicio sze-rint int tıpusu visszateresi erteket tetelez fel a fordıto – ez sok baj forrasalehet), hanem azt is pontosan meg kell adnunk, mennyi, es milyen tıpusuparameterrel rendelkezik egy fuggveny. Ez a szoros deklaracios kenyszer le-hetoseget teremt arra, hogy a C fordıto figyelmeztessen minket, ha esetleghianyos aktualis parameterlistaval, vagy esetleg nem a deklaracio szerinti(vagy azokkal kompatibilis) tıpusu adatokkal aktivizalunk egy fuggvenyt.Tekintsunk egy peldat az ANSI C szerinti fuggvenydeklaraciora:

Page 21: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

16 2. FEJEZET. BONYOLULTABB SZERKEZETEK

double mypower(double x, double y);

A mypower tehat egy double visszateresi erteket szolgaltato, 2 double pa-rametert varo fuggveny. A deklaraciobol a formalis parameterek azono-sıtoi elhagyhatok. (Termeszetesen a fuggvenydefinicional, tehat amikor afuggvenytorzset is megadjuk, mar ki kell ırnunk a formalis parameterekazonosıtoit is).

A fuggvenyek kotelezo deklaraciojanak, vagy mas szakkifejezessel elve,a protıpusmegadasnak egy masik elonye is van: az ilyen, ugynevezett ujstılusu fuggvenydeklaraciot alkalmazo C programjaink minden tovabbi nel-kul beilleszthetok egy objektum-orientalt C++ programba, ahol a pontosprototıpusmegadas alapkovetelmeny.

Az uj stılusu fuggvenymegadas mellett letezik azonban a regi stılus is.A regi stılusu deklaracioknal csak a visszateresi ertek tıpusat es a fuggvenyazonosıtojat adjuk meg, a parameterlistarol nem mondunk semmit. Csaka fuggveny definicional adjuk meg a parameterlistat. A fenti fuggvenyunkregi stılusu deklaracioja a kovetkezokeppen nez ki:

double mypower();

A regi stılusu fuggvenydefinicio pedig ıgy kezdodik:

double mypower(x, y)double x, y;

majd ezt koveti a fuggveny torzse.A regi es az uj fuggvenydeklaracios stılus alkalmazasa altalaban kizarja

egymast. Hogy keszıthetunk megis olyan programokat, amelyek akar egyregi C fordıtoval, akar a legujabb fordıtokkal, vagy akar egy C++ fordıtovalis lefordıthato?

Nos, a megoldast termeszetesen a felteteles fordıtas, illetve az egyesnyelvi implementaciok altal elore definialt szimbolumok felhasznalasa je-lenti. Tekintsuk az alabbi peldat:

/** A PROTOTYPES szimb´olum csak a szabv´anyos C ford´ıt´ok* sz´am´ara lesz defini´alva:*/

#undef PROTOTYPES#ifdef __STDC__

/* Ha ANSI C kompatibilis a ford´ıt´o */#define PROTOTYPES 1 /* akkor kell f¨uggv´enyprotot´ıpus */#endif

Page 22: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2.1. AZ ELOFELDOLGOZO HASZNALATA 17

/* A f¨uggv´enydeklar´aci´ok ekkor ´ıgy n´ezhetnek ki: */

double mypower(#ifdef PROTOTYPES

double, double#endif

);

A fenti modon definialt PROTOTYPES szimbolumot a fuggvenydefinicional isfelhasznaljuk:

/* A mypower f¨uggv´eny r´egi ´es ´uj st´ılus´u definici´oja: */

double mypower#ifdef PROTOTYPES

(double x, double y) /* ´uj st´ılus */#else

(x, y) double x, y; /* r´egi st´ılus */#endif{

/* Ide ker¨ul maga a f¨uggv´enyt¨orzs */}

2.1.6. Feladat:

Irjunk egy olyan C nyelvu faktorialis-szamıto programot, amelyben a fel-hasznaloval valo kommunikaciot a main vegzi, es a faktorialis erteket egysajat fuggveny hıvasaval vegzi! A faktorialis szamıto fuggveny ne vegezzenI/O muveletet! Ugy ırjuk meg a fuggvenyeink (main, faktorialis szamıto)definicioit, hogy a programunk mind regi stılusu C fordıtoval, mind pedigprototıpust igenylo fordıtoval lefordıthato legyen! Alkalmazzuk a korabbanleırtakat! A fordıtashoz a Unix cc-t, illetve a C++ uzemmodba kapcsoltBorland C++-t hasznaljuk!

2.1.7. Makrodefiniciok

A #define direktıvat nemcsak fordıtasvezerlesre, illetve szimbolikus kons-tansok definialasra hasznalhatjuk, hanem parameterekkel rendelkezo un.makrok, azaz egyszeru rutinok ırasara is. Peldaul a math.h szabvanyosfejlecfile-ban definialt abs rutin makro megvalosıtasa ıgy nez ki:

#define abs((x)) ((x) > 0 ? (x) : (-(x)))

Page 23: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

18 2. FEJEZET. BONYOLULTABB SZERKEZETEK

Figyeljuk meg, hogy a makro parameteret zarojelekkel vedjuk. Ez azert tor-tenik ıgy, hogy osszetett kifejezesekkel meghıva a makrot, a makrodefinicobehelyettesıtese utan se keletkezhessen hibas kifejezes (lasd precedenciak).

2.1.8. Feladatok: min, max, pow, toupper, tolower

Oldjuk meg a kovetkezo feladatokat!

a.) A fenti minta alapjan keszıtsuk el a min(a,b), illetve a max(a,b)makrokat. Irjunk kiprobalo foprogramot e makrok hasznalatahoz!

b.) Az xy = exp(y · log(x)) osszefugges felhsznalasaval ırjuk meg a mypo-wer(x,y) hatvanyozo makrot! Ugyeljunk arra, hogy a makro az x = 0esetre is helyes eredmenyt (0.0) adjon. Ne feledjuk, hogy a megoldas-hoz szukseges exp, illetve log fuggvenyek double visszateresi erteku-ek! (A math.h szabvanyos fejlecfile-ban vannak deklaralva). Irjunkegy keretprogramot, amellyel kiprobalhatjuk a sajat, mypower hatva-nyozo makronkat!

c.) Probaljuk sajat makrokent megırni a ctype.h fejlecfile-ban definialttoupper, illetve tolower rutinokat! Nevezzuk el a sajat valtozatunkatmytoupper-nek, illetve mytolower-nek. (A parameterkent kapott ka-raktert nagybeture, illetve kisbeture cserelik.) Ugyeljunk arra, hogytenyleg csak a betu karakterek eseteben kell a konverziot elvegezni.

A kovetkezo programpelda a c.) feladatban emlıtett toupper, illetvetolower szabvanyos makrok hasznalatat szemlelteti:

/************************************************************* File: pelda.c ** Tartalom: Kisbet˝u-nagybet˝u felcser´el˝o mintaprogram **************************************************************/#include <stdio.h>#include <ctype.h>

/* A modulban defini´alt f¨uggv´enyek: */void main(void);/* ======================================================== */void main(){

register c;while ((c = getchar()) != EOF){ /* c-be olvasunk, file v´eg´eig */

if (isupper(c)) /* Ha nagybet˝u, akkor.... */

Page 24: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2.2. TOMB-, POINTER- ES FUGGVENYTIPUSOK 19

{c = tolower(c); /* .... kisbet˝ure cser´elj¨uk, */

} else /* .... egy´ebk´ent pedig .... */{

c = toupper(c); /* .... nagybet˝ure cser´elj¨uk. */} /* .............. Az ’if’ utas´ıt´as v´ege ...... */putchar(c); /* A megv´altoztatott c-t ki´ırjuk */

} /* ................... A ’while’ ciklus v´ege ....... */} /* ....................... A ’main’ blokk v´ege ......... */

Modosıtsuk tehat ezt a peldaprogramot ugy, hogy a szabvanyos makrokhelyett a sajat makroinkat hasznaljak a karakterkonverziora.

2.2. Tomb-, pointer- es fuggvenytıpusok

A C alaptıpusaibol (char, int, float, double) es ezek modosıto jelzokkelkepzett valtozataibol un. szarmaztatott tıpusokat, es ezek felhasznalasavalszarmaztatott tıpusu tarolasi egysegeket hozhatunk letre. Peldaul az intalaptıpusbol letrehozhatjunk egeszre mutato pointer tıpust, az egeszt tartal-mazo tombok tıpusat, illetve egeszt visszaado fuggvenyek tıpusat. Ezekketa tıpusokat felhasznalva letrehozhatunk egeszre mutato pointereket, egeszttartalmazo tomboket, egesz visszateresi erteku fuggvenyeket.

Egy alaptıpusbol ketfelekeppen hozhatunk letre szarmaztatott tıpusutarolasi egyseget:

1. vagy a tarolasi egyseg (valtozo, fuggveny) deklaracioja soran az adotttarolasi egyseggel kapcsolatban adjuk meg, hogy annak tıpusa hogyszarmaztathato a deklaracio alaptıpusabol,

2. vagy pedig altalaban definialjuk a szarmaztatott tıpust a typedefkulcsszo segıtsegevel, es utana az ıgy letrehozott uj tıpusba, mintalaptıpusba tartozo tarolasi egysegkent deklaraljuk a kerdeses objek-tumot.

Peldak az elso esetre:

double d, dtomb[20], *dmut, dfugg(int);

Az alaptıpus a double. Ilyen tıpusu tarolasi egyseg a d valtozo – amely,mint tudjuk, tarteruletfoglalo tarolasi egyseg. A fenti peldaban 2 tovabbitarteruletfoglalo tarolasi egyseget definialunk, ezeknek az azonosıtoi rendredtomb, illetve dmut. Az elobbi egy 20 elemu double alaptıpusu tomb, azutobbi pedig egy double-ra mutato (inicializalatlan) pointer. A fenti pel-daban szereplo utolso elem – a dfunc – egy kodgeneralo tarolasi egyseget,

Page 25: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

20 2. FEJEZET. BONYOLULTABB SZERKEZETEK

Operator Megnevezes Jelleg

( ) fuggvenytıpust kepzo operator postfix[ ] tombtıpust kepzo operator postfix* mutatotıpust kepzo operator prefix

2.3. tablazat.Tıpusmodosıto operatorok. A precedencia felulrol lefele csokken.

azaz egy fuggvenyt deklaral. dfunc ”egy double-t visszado, egy int pa-rametert varo” tıpusu fuggveny azonosıtoja. (Vegyuk eszre a kulonbsegeta definicio es a deklaracio kozott: a definicio letre is hozza a tarolasi egy-seget, mıg a deklaracio csak azt mondja meg, hogy milyen tıpusu az illetotarolasi egyseg – egy fuggveny teljeserteku megadasahoz a fuggvenytorzsreis szukseg lenne.)

Egy szarmaztatott tıpus megadasanak a logikaja a kovetkezo: megad-juk a definialando/deklaralando tarolasi egyseg alaptıpusat (ez itt most adouble), majd megadjuk a tarolasi egyseg azonosıtojat, es hozzakapcsolunkegy un. tıpusmodosıto operatort. Termeszetesen egy adott tarolasi egysegazonosıtojahoz nemcsak egy tıpusmodosıto operator kapcsolhato, hanemtobb is:

double d2dimtomb[20][5], **dmutmut, *dmutfunc(int);

Itt d2dimtomb egy 20·5-os, 2 dimenzios double alaptıpusu tomb, dmut-mut egy double-ra mutato pointerre mutato pointer, dmutfunc pedig egydouble-ra mutato pointer visszateresi erteket ado, egy int parameterrelrendelkezo fuggveny. Ez utobbi pelda azt sejteti, hogy nem mindig egysze-ru dolog egy bonyolultabb szarmaztatott tıpus ertelmezese. Ez egyreszt atıpusmodosıto operatorok kulonbozo precedenciaja miatt van ıgy, masreszta * pointertıpust kepzo operator un. prefix operator, mıgy a [ ] es a ( )operator un. postfix operator.

Konnyebben tudunk osszetetteb szarmaztatott tıpusba tartozo tarolasiegysegeket deklaralni, ha a typedef kulcsszo segıtsegevel magukat a szar-maztatott tıpusokat is deklaraljuk, es a bonyolultabb szerkezeteket lepesrollepesre hozzuk letre. A typedef hasznalatanak altalanos semaja a kovet-kezo:

Uj tıpust mindig valamilyen mar meglevo tıpusbol (elemi tıpusbol, struk-turakbol, vagy typedef-fel mar korabban definialt tıpusbol) hozhatunk let-re ugy, hogy megnevezzuk az uj tıpust:

Page 26: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2.2. TOMB-, POINTER- ES FUGGVENYTIPUSOK 21

typedef int ip;

Tehat a fenti peldaban megnevezett uj tıpus az ip. Vegyuk eszre, hogya fenti tıpusdeklaracio olyan, mintha ip ”typedef” tarolasi osztalyu inttıpusu valtozo lenne. Persze nem az, hanem csak az int tıpussal megegye-zo ertelmu ujabb tıpus azonosıtoja. A valtozodeklaracioval analog logikatfolytatva, alakıtsuk at ip ertelmezeset. Legyen ip peldaul egy int-re mu-tatu pointer tıpusanak az azonosıtoja:

typedef int *ip;

Kovetkezo peldankban egy egesz tıpust visszaado, ket egesz parametertvaro fuggveny tıpusat definialjuk:

typedef int ifunc(int, int);

Ebbol a tıpusbol most – fenti logikat kovetve, a megfelelo tıpusmodosı-to operator segıtsegevel – egy pointertıpust szarmaztatunk:

typedef ifunc *ifuncptr;

Megjegyzendo, hogy a tıpusmodosıto operatorok a szo szorosan vett er-telmeben veve nem operatorok, mert nem valamilyen adathalmazon ertel-mezett muvelet vegrehajtasara szolo utasıtast jelentenek, hanem csak a Cforrasprogramok fordıtasakor van szerepuk.

Termeszetesen a tıpusmodosıto operatoroknak van vegrehajthato muve-letet jelento parjuk. Ezek a * indirekcio operator, a [ ] indexelo operatores a ( ) fuggvenyaktivizalo operator. Osszefoglalva az eddigieket:

Alaptıpusu Tıpus- Szarmaztatott Alaptıpust Alaptıpusutarolasi egyseg modosıto tıpusu tarolasi kepzo kifejezes

operator egyseg operator ertelmezese

char ch=’a’; * char *chp=&ch; * *chppointer- indirekcio az a karakter,tıpust pointer amire chp

kepzo op. mutat (az ’a’)

int i=100; ( ) int ifv(); ( ) i = ifv();fuggveny- fv.hıvas az ifv fuggveny

tıpust fuggveny altal visszaadottkepzo op. egesz

float f=3.14; [ ] float fvek[5]; [ ] f=fvek[1]tomb- indexeles fvek[1] egytıpust tomb float szam

kepzo op. fvek 2. eleme

Page 27: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

22 2. FEJEZET. BONYOLULTABB SZERKEZETEK

Megjegyzesek:

1. A C-ben a tombok indexelese mindig 0-tol kezdodik. A C-ben nincsautomatikus indexhatar ellenorzes.

2. Egy tombvaltozo azonosıtoja onmagaban, mint kifejezes, a tomb kez-docımet jelenti, tehat alaptıpus* tıpusu kifejezes. Peldaul a

char string[ ] = "Ez egy sztring";

definicioju karaktertomb (melynek merete az inicializalo sztringkons-tans meretevel fog megegyezni) ugy is felfoghato, mint egy inicializaltkarakterpointer konstans:

const char *string = "Ez egy sztring";

ahol a pointerkonstansnak kezdoertekul a sztringkonstans kezdocımetadtuk. Ezek alapjan altalaban ertelme van az alabbiaknak:

char str1[ ] = "abcdefghijklmnopqrstuvwxyz";char *str2;char str3[sizeof(str1)/sizeof(char)];...str2 = str1;...*(str3 + 2) = str2[2] = ’C’;

Tehat pointernek tombcım adhato kezdoertekul, illetve az indexelooperator alkalmazhato pointerkifejezesekre is.

3. Akar tombvaltozokra, akar pointerekre alkalmazhato az un. pintera-ritmetika.

Muvelet Eredmeny

pointer + int pointerpointer - int pointerpointer - pointer int

4. A C-ben a fuggvenyek mindig ertek szerint veszik at parametereiket.Egy fuggvenyparameterkent megadott tomb valojaban a tomb kezdo-cımet jelenti. Tehat peldaul a

Page 28: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2.3. KARAKTERTOMBOK ES POINTEREK 23

char *strcpy(char d[ ], char s[ ]);

formaju fuggvenydeklaracio, es a

char *strcpy(char *d, char *s);

formaju fuggvenydeklaracio lenyeget tekintve egyenerteku. Az elsoesetben talan jobban latszik, hogy tomboket szeretnenk parameter-kent atadni.

5. Egy fuggvenyazonosıto onmagaban, mint kifejezes, a fuggvenyt alkotoprogramkod cımet jelenti. Altalaban ugy hasznaljuk, hogy egy fugg-venyekre mutato pointernek adjuk ertekul. Erre vonatkozo peldat egykicsit kesobb mutatunk be.

6. A tıpusmodosıto operatorokra vonatkozo ismereteket a 2.3. tablazat-ban foglaltuk ossze.

2.3. Karaktertombok es pointerek

2.3.1. Feladat: sajat strcpy

Probaljuk megırni a string.h szabvanyos fejlecfile-ban deklaralt strcpyfuggvenyt! A fuggvenynek ket parametere van, mindketto egy-egy karak-tertomb. A fuggveny feladata, hogy a masodik parameterkent kapott sztrin-get az elso parameterkent kapott tombbe masolja be. Visszateresi ertekula masolat sztring cımet adja!

1. megoldas:

char *strcpy(char d[ ], char s[ ]){ int i,l;

l = strlen(s);for (i = 0; i < l; i++) d[i] = s[i];return &d[0];

}

Itt egyszeruen egy szabvanyos rutinnal (strlen) lekerdezzuk a forras sztringhosszat, majd egy for ciklust l-szer vegrehajtva karakterenkent masolunk.Ugyesebb megoldas, ha kihasznaljuk, hogy a sztringek veget mindig az EOSkarakter jelzi. Az EOS figyelesevel a sztring hossza erdektelenne valik. Ezt

Page 29: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

24 2. FEJEZET. BONYOLULTABB SZERKEZETEK

szemlelteti a

2. megoldas:

char *strcpy(char d[ ], char s[ ]){ int i;

while((d[i] = s[i]) != EOS) i++;return d;

}

Ennel jobb megoldast adhatunk, ha kihasznaljuk, hogy a tombparametereka fuggvenyeknak valojaban pointerkent lesznek atadva.

3. megoldas:

char *strcpy(char *d, char *s){

char *p = d;

while ((*d = *s) != EOS) { d++; s++; }return p;

}

Vegul azt is kihasznalhatjuk, hogy az indirekcio operatora (*) magasabbprecedenciaju, mint a pointerekre alkalmazott ++ operatore, tovabba ki-hasznalhatjuk az is, hogy az EOS decimalis erteke 0, azaz logikai ertelem-bem hamis, ennelfogva a relacios muveletre nincs is igazan szukseg a whileciklusbol valo kilepes eleresehez.

4. megoldas:

char *strcpy(char *d, char *s){ char *p = d;

while ((*d++ = *s++));return p;

}

Page 30: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2.4. FUGGVENYPOINTEREK 25

2.3.2. Feladat: sajat strlen

Az elozo feladat megoldasa soran tett megfontolasokat alkalmazva probal-juk mi magunk megırni a string.h-ban deklaralt strlen fuggvenyt! Afuggveny bemenoprameterkent egy sztring kezdocımet kapja, visszateresiertekul a sztring zaro EOS nelkuli hosszat adja.

2.4. Fuggvenypointerek

Fuggvenyekre mutato pointerekre sok esetben szuksegunk lehet. Gondol-junk csak arra, hogy egy numerikus intgeralast vegzo rutinnak tetszolegesintegralando fuggveny eseteben mukodnie kell, lehetoleg valtoztatas nelkul.Nos, erre a C kivalo lehetosegeket nyujt. Mielott azonban egy univerzalisintegralo rutint ırnank, tekuntsunk egy egyszerubb peldat.

Felhasznalas: qsort

Tegyuk fel, hogy egy adott tıpusu adathalmazt valamilyen szempont szerintrendeznunk kell. Legyen ez az adathalmaz mondjuk egy tombben adott.Felmerulhet bennunk, hogy az elso felev soran megismert valamelyik rende-zo algoritmust mi magunk lekodoljuk C-ben, es ezzel a problema meg is vanoldva. Nos, ez egy jarhato ut, de ket ellenvetesunk is lehet. Az egyik az,hogy az adatrendezesre sok elore elkeszıtett rutin letezik, ugyhogy nagy va-loszinuseggel idot es munkat pazarlunk a sajat probalkozasunkkal. A masikellenvetes az lehet, hogy ha megis nekilatunk egy rendezo rutin ırasanak,nagy valoszinuseggel az altalunk elkeszıtett valtozat tulsagosan testresza-bott lesz, azt kesobb nehezkes lesz mas programokban felhasznalni.

Az igzsag az, hogy az adatrendezest igen egyszeruen megoldhatjuk azstdlib.h szabvanyos fejlecfile-ban deklaralt qsort fuggveny felhasznalasa-val. Ez a rutin az ismert quicksort (gyorsrendezo) algoritmussal dolgozik.Prototıpusa a kovetkezokeppen nez ki:

void qsort(void *base, size_t nelem, size_t width,int (*fcmp)(const void *elem1, const void *elem2));

Ertelmezzuk az egyes parametereket! Az elso parameter, base a rendezendotomb kezdocıme. Mivel tetszoleges tıpusu adatok johetnek szoba, base-t’altalanos pointertıpusunak’ (void*) deklaraltak. Majd a fuggvenyhıvassoran nekunk kell az un tıpusatalakıto (type cast) operatorral a mi mutato-tıpusunkat void* tıpusuva alakıtanunk. A masodik parameter (nelem) es aharmadik parameter (width) tıpusa size t. Ez egy szabvanyos tıpusjeloles.Lenyegeben ez egy int, de ez az alternatıv nev arra hıvja fel a programozo

Page 31: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

26 2. FEJEZET. BONYOLULTABB SZERKEZETEK

figyelmet, hogy itt az aktualis adattıpusra vonatkozo meretinformaciokatkell megadni. nelem-ben a rendezendo tomb meretet kell megadnunk, mıgwidth-ben egy tombelem meretet varja a rutin sizeof egysegben. A qsortrutin utolso parametere fcomp. Ez egy fuggvenypointer tıpusu parameter.Itt egy olyan fuggveny cımet kell megadnunk, amelyet a qsort a rende-zes soran szukseges elemosszehasonlıtasok elvegzesere hasznalhat fel. Ez afuggveny egesz tıpusu visszateresi erteket kell szolgaltasson. Bemeno pa-rameterkent ket osszehasonlitando tombelemre mutato pointert kap. Avisszateresi erteket a kovetkezokeppen kell szolgaltatnia az osszehasonlıtofuggvenynek:

-1 ha *elem1 < *elem20 ha *elem1 == *elem21 ha *elem1 > *elem2

Lassunk egy peldat a qsort hasznalatara!

#include <stdio.h>#include <stdlib.h>#include <string.h>

int sort_function (const void *a, const void *b);

char list[ ][4] = { "cat", "car", "cab", "cap", "can" };

#define LISTSIZE sizeof(list)/sizeof(char*)#define ELEMSIZE sizeof(list[0])/sizeof(char)int main(void){

int i;

qsort((void*)list, LISTSIZE, ELEMSIZE, sort_function);for (i = 0; i < LISTSIZE; printf("%s\n", list[i++]);return 0;

}/* ----------------------------------------------------- */int sort_function(const void *a, const void *b){

return strcmp((char*)a,(char*)b);}/* ----------------------------------------------------- */

Page 32: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2.4. FUGGVENYPOINTEREK 27

Tehat a list nevu, 4 karakteres sztringekbol allo tombot szeretnenk no-vekvo sorrendbe rendezni. list elso dimenziojanak a meghatarozasat afordıtora bızzuk – hiszen ez az adat az inicializalo lista alapjan egyertel-muen kiderul. Ezt az erteket a sizeof operator felhasznalasaval ki is sza-moltatjuk, es LISTSIZE szimbolumhoz rendeljuk, hogy kesobb egyszeruenhasznalhassuk. Ugyancsak a preprocesszorral szamoltatjuk ki egy listaelemmeretet, es ezt az ELEMSIZE szimbolumhoz rendeljuk.

Ezeket a hozzarendeleseket persze megelozte az osszehasonlıto sort func-tion fuggvenyunk deklaralasa. Vegyuk eszre, hogy e fuggveny tıpusa meg-egyezik a qsort formalis parameterlistajan szereplo *fcmp tıpusaval.

A qsort rutin meghıvasakor csak list-et kellett void* tıpusuva kon-vertalnunk. Utolso parameterkent szerepel a hasonlıto fuggveny cime –sort function – osszhangban az 5. megjegyzesben leırtakkal.

Jelen peldankban a hasonlıto fuggvenyt igen konnyen elkeszıthettuk.Mivel sztringkonstansokat kellett egymassal osszehasonlıtanunk, egyszeru-en a string.h szabvanyos fejlecfile-ban deklaralt strcmp fuggvenyt hasz-nalhattuk, mert ennek parameterezese es visszateresi erteke megfelel a qsortaltal megkıvantaknak. Tulajdonkeppen kozvetlenul is megadhattuk volnastrcmp-t a qsort hıvasakor, de ekkor megoldasunk nem lett volna korrekt:*fcmp-nel void* tıpusu bemeno parameterek vannak eloırva, mıg strcmpparameterei char* tıpusuak. A mi sort function fuggvenyunknek tehatsemmi mas dolga nincs, mint ezt a tıpuskonverziot vegrehajtani.

Feladat: Definialjunk egy tetszoleges tıpust (lehet akar a Pascal RECORD-nak megfeflo struct is), ebbol hozzunk letre egy rendezetlenul kitoltotttombot, majd rendezzuk a qsort rutinnal. Irjuk meg a rendezeshez szuk-seges hasonlıto fuggvenyt. E fuggveny modosıtasaval valtoztassuk meg arendezesi szempontot!

Indirekt fuggvenyhıvas

Lattuk, hogy a fuggvenypointerek hasznalata nagy flexibilitast tud kolcso-nozni kesz rutinok szamara. Ezt hasznaljuk ki arra, hogy egy integralofuggvenyt ırjunk a felev elejen megismert valamelyik numerikus integraloalgoritmus felhasznalasaval. Az integralo fuggveny deklaracioja a kovetkezolegyen:

double integration(double a, double b, int n,double (*f)(double x));

Irjunk olyan foprogramot, amely egy-ket ismertebb fuggvenyosztalyba tar-tozo fuggveny integraljat szamolja ki! A szukseges foprogram egy lehetsegesmegvalosıtasat a kovetkezo oldalon talalhatjuk. Tekintsuk at alaposan ezt

Page 33: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

28 2. FEJEZET. BONYOLULTABB SZERKEZETEK

a listat. Probaljuk megerteni tpedef utasıtasokat, illetve az integranduszfuggvenyek deklaralasanak kisse szokatlan modjat. (Ez utobbival kapcso-latban tessek arra gondolni, hogy a tıpusmodosıto operatorokkal kapcsolat-ban leırt logikat kovetve, a kodgeneralo tarolasi egysegeket – a fuggvenye-ket – ugyanugy deklaraljuk, mint a tarteruletfoglalo tarolasi egysegeket.Ha tehat a double-t visszaado, egy doble parametert varo fuggveny tıpusttypedef-fel definialjuk – ez a mi esetunkben a dfunc tıpus – akkor ennekfelhasznalasaval tarolasi egysegeket deklaralhatunk. Megjegyzendo, hogyfuggvenydefiniciot mar nem vegezhetunk typedef-fel definialt fuggvenytı-pus segıtsegevel.

#include <math.h>typedef double dfunc(double x);typedef dfunc *dfp;/* ----------------------------------------------------- */double integration(double, double, int, dfp);/* ----------------------------------------------------- */dfunc expon, /* a+b*exp(c*x) */

power, /* a*x^y */sinus, /* a+b*sin(c*x+d) */polin; /* a+b*x+c*x^2+d*x^3 */

/* The same as double expon(double x), power(double x),sinus(double x), polin(double x); */

/* ----------------------------------------------------- */dfp functions[ ] = { expon, power, sinus, polin };char *fstrings[ ] = { "a+b*exp(c*x)", "a*x^y",

"a+b*sin(c*x+d)", "a+b*x+c*x^2+d*x^3"};

double a = 0, b = 0, c = 0, d = 0;/* ----------------------------------------------------- */int main(void){ int i = -1;

double xa, xb;

while ((i < 0)||(i > 3)){ printf("0 - %s\n1 - %s\n2 - %s\n3 - %s\n\n",

fstrings[0],fstrings[1],fstrings[2],fstrings[3]);scanf("%d",&i); putchar(’\n’);

}switch(i){

Page 34: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2.4. FUGGVENYPOINTEREK 29

case 2:case 3: printf("d="); scanf("%lf",&d); putchar(’\n’);case 0: printf("c="); scanf("%lf",&c); putchar(’\n’);

printf("b="); scanf("%lf",&b); putchar(’\n’);case 1: printf("a="); scanf("%lf",&a); putchar(’\n’);

}printf("xa="); scanf("%lf",&xa); putchar(’\n’);printf("xb="); scanf("%lf",&xb); putchar(’\n’);printf("\nIntegral of %s = %12.5g\n",fstrings[i],

integral(xa,xb,50,functions[i]));}/* ----------------------------------------------------- */

double polin(double x){

return a + b*x + c*x*x + d*x*x*x;}.../* ----------------------------------------------------- */double integration(double a, double b, int n,

double (*f)(double x));{ double integr, x, dx;

for (integr = 0, x = xa, dx = (xa-xb)/n; n; n--, x+=dx){

integr += (*f)(x) * dx;}return integr;

}/* ----------------------------------------------------- */

Feladatok: Integralas fuggvenypointerrel adott integranduszok-kal

a) Dolgozzuk ki teljesen az integralo programot, futtassuk le!

b) Bovıtsuk az integralhato fuggvenyosztalyok halmazat!

c) Alakıtsuk at a programot ugy, hogy az integration fuggveny egytovabbi int tıpusu parameterevel valaszthassunk kulonbozo integra-lasi modszerek kozott. Ezt szinten indirekt (fuggvenypointer tombonkeresztul torteno) fuggvenyhıvassal valosıtsuk meg!

Page 35: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

30 2. FEJEZET. BONYOLULTABB SZERKEZETEK

2.5. Tobb modulbol allo programok keszıtese

Az elozo feladatok megoldasokar feltunhetett, hogy milyen buta dolog az,ha olyan modosıtasokat vegzunk, aminek semmi koze az integration fugg-venyhez, azt akkor is ujra kell fordıtanunk a valoszinuleg szinten valtozatlanmain-nel egyutt. Vagy ha a numerikus integralast vegzo fuggvenyek kor-nyeken tortent valtozas, akkor az integralando fuggvenyeket fordıtjuk ujra.

Ezen csak ugy tudunk valtoztatni, ha a forrasallomanyunkat hatekonymodon atszervezzuk: tobb, egymastol fuggetlenul fordıthato modulra bont-juk, es a vegso futtathato program osszeallıtasat az adott C nyelvi rendszerlinkelo programjara bızzuk. Milyen reszekre erdemes bontani a programun-kat?

Celszeru az alabbi felosztast kovetni:

• mainint.c A main fuggvenyt (foprogramot) tartalmazza.

• myfunc.c A sajat, integralando fuggvenyek (peldaul polin, expon,stb) definicoit tartalmazza.

• numint.c A numerikus integralas fuggvenyeit (a kulso modulokkalkapcsolatot tarto integration fuggvenyt, valamint az egyes belsointegralo fuggvenyeket – ilyen lehet mondjuk egy simpson nevu fugg-veny) tartalmazza.

• myvars.c A tobb modul altal is hasznalt publikus globalis valtozokdefinicioit tartalmazza.

Mar majdnem optimalis a forraspallomany felosztasa. A fenti felosztastenyleg a program optimalis modulstrukturajanak felel meg, de a forrasallo-manyt meg celszeru tovabb tagolni. Ennek a tovabbi tagolasnak a celja az,hogy minden .c kiterjesztesu file-bol generalt modul ugyanazokat a dekla-raciokat lassa, illetve az egyes modulok ezen deklaraciok utjan kapcsolatbanlegyenek egymassal. Igy a kovetkezo un. deklaracios fejlecfile-okat celszerumeg letrehozni:

• mytypes.h A sajat tıpusdefinicioinkat (dfunc, dfp tartalmazza. Eztminden .c forrasallomany elejere epıtsuk be az #include "mytypes.h"preprocesszor utasıtassal!

• myfunc.h Az egyes .c allomanyokban definialt publikus fuggvenyekextern deklaracioit tartalmazza. Ezt minden forrasallomany elejere,a mytypes.h utan epıtsuk be az #include "myfunc.h" preprocesz-szor utasıtassal!

Page 36: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

2.5. TOBB MODULBOL ALLO PROGRAMOK KESZITESE 31

Elettartam Lathatosag Deklarator helye Tarolasi osztalystatikus globalis barmely modulban extern

minden blokkon kıvulstatikus modulra adott modulban static

lokalis minden blokkon kıvulstatikus blokkra adott blokkban static

lokalisdinamikus blokkra adott blokkban auto,

lokalis register

2.4. tablazat. Valtozok elettartama, lathatosaga es tarolasi osztalya

• myvars.h A myvars.c allomanyban definialt publikus globalis val-tozok extern deklaracioit tartalmazza. Ezt minden fuggvenyeket de-finialo forrasallomany elejere, a myfunc.h utan epıtsuk be az #inc-lude "myvars.h" preprocesszor utasıtassal!

A valtozodejklaraciok, illetve definiciok helyes kialakıtasaban nyujthatsegıtseget a tarolasi osztalyokat osszefoglalo 2.4 tablazat.

Feladat: A fenti elveknek megfeloen szervezzuk at programunkat, ku-lon-kulon fordıtsuk le az egyes .c forrasmodulokat, majd linkeljuk osszea programot.

Page 37: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

32 2. FEJEZET. BONYOLULTABB SZERKEZETEK

Page 38: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

3. fejezet

A dinamikus tarkezelesalapjai

3.1. Dinamikus adatok

Pointereknek ertekul eddig vagy egy valtozo cımet adtuk (a & – address ofoperator segıtsegevel), vagy egy tomb kezdocımet. Azt is lattuk, hogy nincselvi kulonbseg egy tombvaltozo es egy azonos alaptıpusba tartozo pointerkozott. A gondot csak az jelenthette, hogy a C tombok merte – hasonloana Pascal tombokehez – a deklaraciokor (akar altalunk explicit modon, akaregy inicializalo kifejezes altal implicit modon) megadott, kotott ertek. Hogyırjunk ekkor olyan programot, amelyik mindig csak akkora tomboket hasz-nal, emekkorakra tenylegesen szukseg van a programfutas soran, es a marnem szukseges tombok altal elfoglalt memoriateruletet fel is szabadıtja?

A fenti problemat szamunkra az stdlib.h szabvanyos fejlec file-bandeklaralt malloc memoriafoglalo, illetve a free memoriafelszabadıto fugg-venyek oldjak meg. E fuggvenyek prototıpusai ıgy neznek ki:

#include <stdlib.h>

void *malloc(int size);void free(void*);

A malloc fuggveny size darab byte tarolasara szolgalo memoriateruletetker az operacios rendszertol. Ha a keres teljesıtheto, akkor lefoglalja azadott memoriablokkot, es visszateresi ertekul egy, a blokkra mutato alta-lanos tıpusu (void*) pointer erteket ad. Ha a memoriafoglalasi keres nemteljesıthetu, visszateresi ertekul NULL-t kapunk. A C-ben a NULL ugyanazt

33

Page 39: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

34 3. FEJEZET. A DINAMIKUS TARKEZELES ALAPJAI

jelenti, mint a Pascal-ban a NIL: azaz ez a sehova se mutato pointer, amelyminden ervenyes memoriacımtol egyertelmuen megkulonboztetheto.

Tekintsunk egy peldat! Keszıtsunk egy n meretu double tıpusu tombot!A tomb meretet a felhasznalo adja meg!

#include <stdlib.h>...int n;double *tombmut;...printf("Size="); scanf("%d",&n); putchar(’\n’);

tombmut = (double*) malloc(n * sizeof(double));if (tombmut == NULL){

printf("Unable to allocate for %d double numbers!\n",n);exit(1);

}...for (i = 0; i < n; i++){

tombmut[i] = 0.0;}...free(tombmut);...

A fenti peldaban a kovetkezo erdekesebb megoldasokat alkalmaztuk.

1. Eloszor is a malloc altal visszaadott pointer kifejezes tıpusat az un.type-cast operatorral olyan tıpusuva alakıtottok, amilyen tıpusu pon-terre konkretan szuksegunk van. A type-cast operator altalanos alak-ja: (uj tıpus). Hatasa az, hogy operandusanak tıpusat uj tıpussaalakıtja. A mi konkret esetunkben az altalanos mutatotıpusbol, avoid*-bol csinaltunk double-ra mutato tıpust double*-ot.

2. A malloc szamara meg kell adnunk a lefoglalando memoriablokkbyte-okban kifejezett meretet. A felhasznalotol mi csak a tomb logikaimeretet kerdezzuk meg (ez az n), a tenyleges fizikai meret megalla-pıtasahoz a logikai meretet meg kell szorozni a tomb alaptıpusanakbyte-okban kifejezett meretevel. Ez utobbi adatot a sizeof opera-torral allıtottuk elo.

Page 40: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

3.1. DINAMIKUS ADATOK 35

3. Termeszetesen a malloc altal visszaadott pointer-kifejezest meg kellvizsgalnunk, hogy nem NULL-e? Ha NULL, akkor a memoriafoglalasikerelem nem volt teljesıtheto. Ez sokszor fatalis programfutasi hibatjelez, ıgy a megfelelo hibauzenet kiratasa utan az exit fuggveny fel-hasznalasaval felbeszakıtjuk a programfutast, es az operacios rendszerszamara egy megfelelo hibakodot atadunk. (Ezt a kodot megvizsgal-hatja a programot aktivizalo parancs-file, vagy batch-file.) Vegyukeszre, hogy a Pascal NIL-hez hasonloan a NULL tetszoleges pointer-tı-pussal kompatibilis.

4. Ha sikeres volt a memoriafoglalas, akkor a malloc altal visszaadott, esmegfelo tıpusuva alakıtott pointerkifejezesre ugy tekinthetunk, mintegy tomb baziscımere, ıgy akar indexelhetjuk is, mint az igazi tom-boket. (Annyi a kulonbseg az igazi tombokhoz kepest, hogy tombmuterteke megvaltoztathato.

5. Egy C program memoria-hasznalata attol lesz igazan dinamikus, hogya nem hasznalt memoraietruletek felszabadıtja a program. A mi egy-szeru peldankban ezt az utolso utasıtas, a free(tombmut) vegzi el.Fontos, hogy ha a free fuggvenynek nehogy NULL erteku pointer-ki-fejezest adjunk at, mert kulonben ”elszallhat” a programunk.

3.1.1. Feladat: linearis egyenletrendszer megoldasa

Irjunk egy linearis egyenletrendszer megoldasara alkalmas programot! Aprogram kerdezze meg az ismeretlenek szamat, es futasi idoben foglajonhelyet az egyutthato matrixnak, a konstans vektornak, illetve az ismeretle-nek vektoranak! Az eredmenyek kiiratasa utan, de meg a visszateres elottszabadıtsuk fel a lefoglalt memoriat!

Segıtseg a megoldashoz:

Celszeru egy mar letezo programot (akar Pascal, akar C) atırni, illetve mo-dosıtani. A vektorok szamara a helyfoglalast a bevezeto isemerteto alapjankonnyen megvalosıthatjuk. Gondot csak a 2 dimenzios egyutthato matrixjelenthet. Tegyuk fol, hogy statikus helyfoglalas eseteben a matrix dekla-racioja a kovetkezo:

#define N 3

double amat[N][N];

Az amat tombot ugy is felfoghatjuk, mintha az alabbi modon lenne dekla-ralva:

Page 41: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

36 3. FEJEZET. A DINAMIKUS TARKEZELES ALAPJAI

#define N 3

double sor0[N],sor1[N],

sor2[N];double *amat[] = { sor0, /* ez mind double* tipusu */

sor1,sor2

};

azaz amat nem mas, mint double* tıpusu pointerek tombje. Itt meg min-den statikus – az egyes sorok mertet explicit modon definialtuk, mıg amatmeretet implicit modon, az inicializalo kifejezes adja meg. Latjuk tehat,hogy amat egy double-ra mutato pointerek tombjenek kezdocime, maga isegy – igaz, konstans – pointer, olyan, mintha double** tıpusunak deklaral-tuk volna. Ennek alapjan felırhatjuk most mar a dinamikus helyfoglalasraalkalmas deklaraciot is:

double** amat; /* Sehova nem mutat, de majd fog! */

Maga a dinamikus helyfoglalas ket reszbol rakhato ossze. Eloszor az amatnevu, n elemu (n futasi idoben megadott egesz parameter – az aktualisismeretlenek szama) duplapontossagu valos szamokra mutato pointerek ta-rolasar szolgalo tombot hozzuk letre az amat = ((double*)*) malloc(n* sizeof(double*)); utasassal, majd minden egyes sor szamara fogla-lunk helyet. Peldaul az i-edik sort az amat[i] = (double*) malloc(n *sizeof(double)); utasıtassal hozhatjuk letre.

A matrix altal elfoglalt memoriaterulet felszabadıtasakor fordıtott sor-rendet kell kovetnunk. Eloszor az egyes sorokat szuntetjuk meg, majdmagat az amat tombot.

Fontos figyelmeztetesek:

1. Attol, hogy egy pointert deklaraltunk, meg nem lesz erteke, ıgy seho-va sem mutat!

2. Attol, hogy egy pointernek van erteke, azaz mutat valahova, meg min-dig nem biztos, hogy ervenyes memoria-teruletre mutat. Azt a me-moriateruletet, ahova egy pointerrel mutatni szeretnenk, LE KELLFOGLALNI!

3. A C-ben az indexeles 0-tol indul, es tombmeret - 1-ig tart. A tombtul-cımzes miatt nem szol a fordıto, legfeljebb elszall a program. Komo-lyabb operacios rendszerekben (VMS, UNIX) maga az operacios rend-szer figyelmeztet arra, hogy ervenytelen memoricımre hivatkozunk.

Page 42: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

3.1. DINAMIKUS ADATOK 37

Altalaban access violation hibauzenet es rutin-hıvasi lista (symbolicstack dump) kisereteben a programfutast megszakıtja az operaciosrendszer. Sajnos a DOS ilyesmire nem figyel!

Page 43: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

38 3. FEJEZET. A DINAMIKUS TARKEZELES ALAPJAI

Page 44: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

4. fejezet

Az operacios rendszerrelvalo kapcsolat

4.1. Folyam jellegu I/O

Tekintsuk a korabbrol mar ismert, a szabvanyos bemeneti allomanyt a szab-vanyos kinenetre masolo programot!

#include <stdio.h>

main(){

int ch; /* int and char are compatible */

ch = getchar();while (ch != EOF){

putchar(ch);ch = getchar();

}}

Most mar tudjuk, hogy itt egyszeruen arrol van szo, hogy az stdin eloredefinialt folyambol az stdout elore definialt folyamra masolunk. Ezek afolyamok – hacsak az operacios rendszer szintjen at nem iranyıtottuk oket– a billentyuzethez, illetve a terminalkepernyohoz vannak hozzarendelve.

39

Page 45: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

404. FEJEZET. AZ OPERACIOS RENDSZERREL VALO KAPCSOLAT

4.1.1. Feladatok: File-ok masolasa

a) Alakıtsuk at a fenti programot ugy, hogy a getchar, illetve a put-char rutinok helyett az int fgetc(FILE *stream), illetve az intfputc(int c, FILE *stream) deklaracioju fuggvenyekkel kezeljukaz stdin-t es az stdout-ot!

b) Alakıtsuk at az a) pont szerint kidolgozott programot ugy, hogy azINPUT.TXT fizikai allomanyt az OUTPUT.TXT fizikai allomanyba ma-soljuk at. (Feltehetjuk, hogy mindket allomany az aktıv konyvtarbanvan.)

4.1.2. File-nyitasi hibak, azok kezelese

Az elozo b) feladat megoldasa soran felmerulhet a kerdes: mi van akkor,ha az input adatallomany nem letezik? Nos, ez konnyen ellenorizheto. Eztszemleltetjuk az alabbi programreszlettel:

#include <stdio.h>...FILE *finp;char inpfname[80];...

printf("Name of the input file="); scanf("%s%,inpfname);putchar(’\n’);...finp = fopen(inpfname,"r");if (finp == NULL){

printf("File %s does not exist!\n",inpfname);}

A mechanizmus hasonlo, mint amit a dinamikus tarfoglalasnal kozolt prog-ramreszletnel lathattunk. A felhasznalotol bekerjuk a megfelelo parametert– ez itt a file neve; az ıgy megadott sztring az adott operacios rendszerbenervenyes teljes file-specifikacio lehet (drive, directory, filename, extension,version ugy, ahogy azt az operacios rendszer megkıvanja). Ezutan kovet-kezik az operacios rendszerrel torteno kapcsolatfelvetel. Most memoriafog-lalas helyett file-hozzarendeles tortenik. Az eziranyu rendszer-szolgaltatasigenybevetelenek eredmenyet termeszetesen mindig meg kell vizsgalnunk:sikerult-e letrehoznunk azt, amit akartunk. Most azt nezzuk meg, hogysikeres volt-e a file-megnyitas. Ha igen, akkor az un. file-pointer nem NULLerteket kap, ha sikertelen volt, akkor NULL lesz az erteke.

Page 46: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

4.2. A MAIN ARGUMENTUMAI 41

A sikertelen file-megnyitasnak kulonbozo oka lehet, a leggakoribb azon-ban az, hogy az olvasasra megnyitott file nem letezeik. A hiba pontos okarolis szerezhetunk informaciot, de ennek targyalasara nem futja az idonkbol.A lenyeg az, hogy minden un. file-megnyitas eredmenyet meg kell vizsgal-nunk! (Ugyanıgy, ha ırasra nyitunk meg egy file-t, akkor is elkepzelheto,hogy NULL-t kapunk visszateresi ertekul; peldaul akkor, ha nemletezo konyv-tarra hivatkztunk a file-specifikacioban.)

Feladat: Modosıtsuk a file-masolo programunkat ugy, hogy mind az in-put, mind az output file nevet a felhasznalo adja meg. Keszuljunk fel arra,hogy esetleg hibas file-specifikaciot ad meg a felhasznalo!

4.2. A main argumentumai

A main fuggvenynek elore definialt argumentumai lehetnek – ezek az ope-racios rendszer altal a programnak atadott parameterek:

main(argc, argv)int argc;char *argv[];

Az argc azt mutatja meg, hogy az operacios rendszer parancsertelmezojebenhany parametert adtunk at a programnak. Ezek a parameterek mindigsztringkent kerulnek atadasra, a main masodik argumentuma, argv rendreezekre a sztringekre mutat. Megjegyzendo, hogy argv[0] mindig a pro-gram nevet tartalmazza (teljes file specifikacioval), es ennek megfeleloenargc erteke mindig legalabb 1. Ha tehat a DOS-ban a myprog.exe, Cnyelven megırt program a C:\mydir konyvtarbol lett behıva, akkor a pro-gramon belul argv[0] erteke C:\MYDIR\MYPROG.EXE" lesz.

4.2.1. Feladat: A copy parancs – sajat kivitelben

Alakıtsuk at a file-masolo programot ugy, hogy elso parancs-sor parameterekenta forras-file nevet, masodik parameterkent pedig a cel-file nevet vegye ataz operacios rendszertol. A program neve legyen mycopy! Ugy ırjuk mega mycopy programot, hogy ha 0 parameterrel hıvtak meg (azaz argc ==1), akkor egy rovid helpet ırjon ki a kepernyore a hivasi modjarol, majd aFrom: kerdessel kerdezze meg az input file nevet, a To: prompttal pedig azoutput file nevere kerdezzen ra. Ha a felhasznalo a From: file-t a paranc-ssorban megadta, es csak a To: file hianyzik (argc == 2), akkor csak arra

Page 47: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

424. FEJEZET. AZ OPERACIOS RENDSZERREL VALO KAPCSOLAT

kell rakerdezni. Minden esetben ellenorizzuk, hogy letezik-e a From: file!(A joker karakterek, illetve a reszleges output file specifikacio kezelesetoleltekintve ez a mycopy program a UNIX cp, illetve a VMS es a DOS 5.0copy parancsanak a kevereke.)

Fontos megjegyzes a VMS-ben dolgozok szamara! A VMS elsokozelıtesben nem tamogatja, hogy a felhasznaloi programoknak parancs-sorparametereket adhassunk at. Ez azt jelenti, hogy a parancs-sor parametereketvaro programok a VMS RUN parancsaval nem futtathatok. A dolog megsemkatasztrofalis: termeszetresen lehteoseg van a C konvencio szerinti parancs-sor parameterek atadasara is. A fenti myprog program futtatasa elott adjukki a

myprog:=="myprog.exe"

parancsot a VMS-nek Ezutan egyszeruen a myprog from.txt to.txt be-gepelesere a myprog program megkapja a parancs-sor parametereket, ıgy afrom.txt file-t a to.txt allomanyba probalja atmasolni – felteve persze,hogy maga a C program hibamentes!

Page 48: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5. fejezet

Fejlettebb technikak

5.1. Strukturak – lancolt lista

5.1.1. Fealadat: Lancolt lista keszıtese

Oldjuk meg C-ben a kovetkezo feladatot! Egy legitarsasag szamıtogepentarolja az utaslistakat. Az osszesıtett allomany (UTAZAS.DAT) minden utasminden utjarol egy-egy bejegyzest tartalmaz. Egy bejegyzes szerkezete akovetkezo:

Jarat szama – 8 karakterDatum – 6 karakterUtas neve – 32 karakterLegi km – egesz szamJelleg – Szolgalati vagy Egyeni

Keszıtsen olyan programot, amely kikeresi, es a TORZS.DAT nevu szovegesallomanyba kiırja a maganuton legtobbet repult ket utas nevet, valamintlegutobbi utjuk datumat. A bemeno allomanyt a fscanf fuggvennyel olvassa,a kimeno allomanyba szepen tabulalva az fprintf fuggvennyel ırjon. A be-meno allomany magnes-szalagon van, ıgy csak egyszer olvashato be. (Peldaulegy VAX-on az MSA0: nevu eszkozon – a szalagegysegen – talalhato azUTAZAS.DAT allomany.)

Segıtseg a megoldashoz: Nyilvanvalo, hogy az input file-bol valamilyenstruc tıpusu (a Pascal RECORD-nak megfelelo) adatokbol kepzett lancoltlistaba kell beolvasnunk az utasok adatait. Azert kell a dinamukis adatkezeles,mert az input file-t csak egyszer olvashatjuk be, nem tudjuk a meretet, vis-zont a mar beolvasott adatokkal rendszeresen ossze kell hasonlıtanunk az

43

Page 49: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

44 5. FEJEZET. FEJLETTEBB TECHNIKAK

ujolag beolvasott adatokat. Nezzuk a szukseges adatstrukturat! —

typedef enum trtyp {private, buisness} travel;

typedef struct flgt {int flgtno; /* flight number */char date[7]; /* date of the flight */char name[32];/* passenger’s name */int flghtkm; /* flight km */travel type; /* 0:prv 1:bsns */flgt *nextrec,/* ->nxt record in lst*/

*prevrec;/* ->prv record in lst*/} psngr_rec;

Figyeljuk meg, mennyivel vilagosabb a lista-szerkezet definicoja a C-ben,mint a Pascal-ban! A C az enum, illetve struct tıpusok deklaraciojakormegengedi az un. tıpuscımke megadasat. A dolog lenyege az, hogy atıpuscımke a tıpusdefinicios blokkot potolhatja. Ezt hasznalhatjuk ki akkor,amikor listaszerkezetek kialakıtasara szolgalo onhivatkzo adatstrukturakathozunk letre. A mi peldankban a nextrec, illetve prevrec olyan pointertıpusu mezoi a psngr rec strukturanak, amelyek psngr rec tıpusu adatok-tra kepesek mutatni.

Egy uj rekord beolvasasa:

psngr_rec *actptr, *wrkptr;int typ, endflag;FILE *finput; /* File pointer to UTAZAS.DAT eg. on

tape device EET751::MSA0: */

.../* We assume that actptr points to a valid data field */endflag = 5;while (endflag == 5){

workptr = (psngr_rec*)malloc(sizeof(psngr_rec));if (workptr == NULL) { printf("Error building the dynamic list\n");

return 1;}

actptr->nextrec = workptr; /* Build the chain */(*workptr).prevrec = actptr;

/* structptr->structfield or

Page 50: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.1. STRUKTURAK – LANCOLT LISTA 45

(*structptr).structfield are the same! */

/* We assume that finput != NULL */

endflag = fscanf{finput,"%d%s%s%d%d",&(workptr->flgtno),workptr->date,workptr->name,&(workptr->flgtkm),&typ); /* read int from file */

workptr->type = (travel)typ; /* make it enum */...

}

Akinek mar jol megy a C, megprobalkozhat ennek a feladatnak a teljes ki-dolgozasaval. A lancolt lista epıtesenek leallasi feltetele az, hogy az inputfile-bol mar nem tudunk olvasni. Ez peldaul ugy derulhet ki, hogy folyam-atosan ellenorizzuk, hogy az fscanf fuggveny mindig a specifikacio szerintiszamu adatot olvasott-e be. Ez a szam a fuggveny visszateresi erteke.

A programbol valo kilepes elott ne feledkezunk meg arrol, hogy illikfelszabadıtanunk a programunk altal lefoglalt memoriat (hasonloan ahhoz,ahogy illik lezarni a lezaratlan file-okat is).

Page 51: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

46 5. FEJEZET. FEJLETTEBB TECHNIKAK

5.2. Menurendszer

Ide jon egy rovidebb menurendszer

Page 52: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 47

5.3. Osszetett mintapelda

Jelen peldank egy igen flexibilis menukezelo rendszer vazat tartalmazza.Az itt felhasznalt megoldasok sokat segıthetnek a tıpusdefiniciokkal, struk-turakkal, pointerekkel, fuggveny-pointerekkel kapcsolatban leırtak meger-teseben.

E pelda fo celja a portabilis programozasi stılus bemutatasa, masresztigyekszunk ravilagıtani arra, hogy egy celszeruen megtervezett adat- esszubrutinstruktura mennyire attekinthetove es konnyen modosıthatova te-szi a felhasznaloi programjainkat. Felhıvjuk az olvaso figyelmet, hogy ezzela peldprogrammal nem azt akarjuk sugallni, hogy ez az igazi menukezelo,illetve felhasznaloi felulet. Leteznek olyan objektum-orientalt konyvtarak,amelyek az itt leırtaknal sokkal fejletteb felhasznaloi feluletet valosıtanakmeg – termeszetesen hasznalatukhoz ismerni kell a C++-t, illetve ha Win-dows alkalmazoi programot keszıtunk, a programvezerlesrol es a menukrolalkotott kepunket mindenkeppen at kell alakıtanunk.

5.3.1. A tervezes egyes fazisai

Minden programfejlesztesi munka soran elojon az a feladat, hogy az adottprogram szamara egy felhasznaloi feluletet (user interface-t) kell ırni. Eza felulet az esetek legnagyobb reszeben valamilyen menurendszert jelent.Egy menurendszert ugy celszeru kialakıtani, hogy az altala nyujtott szol-galtatasok az egesz felhasznaloi programban igenybevehetok legyenek, es afelhasznaloi program tobbi reszeben lehetoseg szerint ne kelljen a kepernyo-es billentyuzetkezelessel foglalkozni. Az egesz felhasznaloi program, illetvea hozzakapcsolodo menurendszer tervezesekor egy masik fontos szempontaz, hogy a program belso vezerlesi szerkezetei tukrozzek azt a vezerlesiszerkezetet, amit a felhasznalo eszlel a program hasznalata soran. Mas-keppen ezt ugy fogalmazhatjuk meg, hogy ne az egyes programfunkciokaktivizaljanak kisebbnagyobb menurutinokat, hanem egy atfogo, hierarchi-kus menurendszer gondoskodjon arrol, hogy mindig a felhasznalo kıvansagaszerinti programfunkciok legyenek aktivizalva.

Portabilitasi megfontolasok

Ha faradtsagos munkaval megtervezunk es letrehozunk egy, a fenti kıvanal-maknak megfelelo felhasznaloi feluletet, celszeru azt ugy programozni, hogyne csak IBM-PC kompatibilis szamıtogepeken, a DOS operacios rendszeralatt, BORLAND C++ fordıtoval lefordıtva fusson, hanem jol korulhata-rolt modosıtasok utan barmely, C fordıtoval rendelkezo geptıpuson, bar-mely operacios rendszeren (pl. VT100-as terminalokkal rendelkezo VAX

Page 53: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

48 5. FEJEZET. FEJLETTEBB TECHNIKAK

gepeken) is hasznalhassuk a megırt rutinjaink tobbseget.Ennek erdekeben celszeru a megırando menurendszert 3 reszre osztani.

Az elso resz tartalmazza a legmagasabb szintu fuggvenyeket, amelyek vatoz-tatas nelkul portabilisak. A masodik, kozbenso szint tartalmazza azokat afuggvenyeket, amelyeknek torzset az aktualis C fordıto es operacios rendszerrendszerfuggvenyei, illetve az aktualis szamıtogep-konfiguracio kepernyojeszerint modosıtani kell. A harmadik, legalacsonyabb szinten celszeru elhe-lyezni a teljesen hardver-specifikus fuggvenyeket. Ilyenek lehetnek peldaulaz IBM PC BIOS rutinhıvasok.

Jelen peldankban csak a legmagasabb szintu reszeit mutatjuk be menu-kezelo rendszerunknek. A masodik, es harmadik csoprtba tartozo fuggve-nyek kozul csak a kozvetlenul felhasznalt fuggvenyek deklaracioit kozoljukrovid magyarazatokkal.

A latvany (look-and-feel) megtervezese

Alapvetoen a BORLAND C++ integralt fejlesztoi kornyezetenek menukon-cepciojat igyekszunk megvalosıtani a hot key-k kivetelevel. Igyekszunk egyegyszeru help-rendszert is megvalosıtani, de nem celunk a BORLAND C++kornyezetfuggo rendszerenek a lemasolasa.

A menurendszert ugy latja a felhasznalo, hogy tobb alfanumerikus ab-lak van a kepernyon. A BORLAND C++ erre tenylegesen is lehetosegetnyujtana, de a hordozhatosag miatt ezt nem hasznaljuk ki. A menuke-zelo rendszerben az osszes karakternyomtato utasıtas az egesz kepernyorevonatkozik, mi magunk figyelunk arra, hogy csak a kepernyo bekeretezettreszen tortenjen nyomtatas. A kepernyon mi magunk hozunk letre kere-tezett reszeket, dobozokat az IBM PC kiterjesztett karakterkeszletevel. Afelhasznalt ’jobb felso sarok’, ’bal felso sarok’, ’fuggoleges vonal’, stb. ka-rakterek egyes szamıtogep terminalokon is leteznek, ”termeszetsen” maskodokkal, ıgy celszeruen ezeket peldaul a #define direktıvaval szimbolu-mokhoz rendeljuk.

Minden menu egy ilyen dobozba kerul, az egyes almenuk dobozai a szu-lo menu dobozatol egy kicsit lejebb kerulnek a kepernyore. Keszıtunk egyfomenu keretet is. Ennek a legfelso sora lesz a fomenu, azaz az egymastolfuggetlen menufak gyokerenek a gyujtohelye. A fomenubol az egyes me-nupontokat vagy egy dedikalt billentyu leutesevel, vagy a kurzor-mozgatonyilak (←, illetve → nyilak) es az Enter billentyu segıtsegevel valaszthat-juk ki. A kivalasztas hatasara a menupont alatt megjelenik a megfeleloalmenu kerete, benne az egyes almenupontokkal. Egy almenuponthoz vagyegy kozvetlenul vegrehajthato programresz, vagy egy tovabbi almenu tarto-zik. Az almenuk pontjait a ↑ ↓ kurzorvezerlo billentyuk es az Enter, illetvededikalt billentyuk segıtsegevel valaszthatjuk ki.

Page 54: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 49

Egy almenubol az Esc, vagy a minden menuben szereplo eXit menu-ponthoz rendelt X billentyu leutesevel lephetunk ki. (Az eXit menupontotes a hozza tartozo X billentyut a portabilitas miatt definialtuk: egyes termi-nalokon az Esc billentyu kodja terminalvezerlo karakterszekvenciak resze,ıgy e billentyu leuteset vagy nem tudjuk erzekelni, vagy a terminal ”meg-bolondul” tole.) Egy menupontkent aktivizalt programreszbol, vagy egyalmenubol visszaterve a hıvo menu kepe mindig regeneralodik, es az utol-jara aktivizalt menupont marad kivalasztva, azaz egyszeruen csak az Enterbillentyu leutesevel ujra aktivizalhato.

Lekepezes adatstrukturakra es vezerlesi szerkezetekre

Az elobb vazolt megjelenes a kepernyon, illetve kezelesi mod azt sugallja,hogy szuksegunk van egy, a fomenut leıro adatstrukturara es az azt ke-zelo fomenu fuggvenyre, illetve letre kell hozni egy olyan adatstrukturat,amellyel leırhatjuk, hogy egy almenu hol helyezkedik el a kepernyon, mi-lyen menupontjai vannak, azokhoz milyen funkcio (milyen vegrehajtandoprogramresz, vagy milyen tovabbi almenu) tartozik, stb.

Nyilvanvalo tehat, hogy kell egy adatstruktura, ami az egyes menupon-tokra vonatkozo informaciokat tartja nyilvan (a menupont neve, a hozzatartozo help-informacio, a hozzarendelt kivalaszto billentyu, kivalasztottak-e, milyen feladatot lat el, esetleges parameter). A menupontokat menulis-takba kell szerveznunk. Egy ilyen listat ki kell egeszıtenunk a kepernyonvalo megjelenesre vonatkozo informaciokkal (milyen hosszu a lista, hanykarakter szeles, a listat tartalmazo doboz hol helyezkedik el a kepernyon,stb.), es megadhatjuk azt is, hogy egy adott menu milyen hierarchia szintenhelyezkedik el a menu-fan.

A look-and-feel-re vonatkozo meggondolasokbol kovetkezik, hogy az al-menuket kezelo menufuggvenyt ugyanolyan funkciokent erdemes felfogni,mint a programunk tenylegesen vegrehajtando egyes reszeit. Igy tehat cel-szeru az egyes menulistakat megszamozni, es a menukezelonk ezen szamalapjan tudja eldonteni, melyik menulistat kell megjelenıtenie es kezelnie.Termeszetesen az is celszeru, hogy az egyes menupontok a vegrehajtan-do programreszletekre vonatkozo default parametereket tartalmaznak, esa menupont kivalasztasakor ezen parameterrel hıvja meg a menukezelo amegfelelo reszprogramot.

Ilyen meggondolasok mellett egy almenu megjelenıtese belso vezerle-si szerkezetkent ugy nyivanul meg, hogy a menukezelo fuggveny onmagathıvja meg ugy, hogy a rekurzıv hıvas alkalmaval az almenu azonosıtojat,mint parametert hasznalja.

Hogy valosıtsuk meg egy adott menuponthoz tartozo fuggveny aktiviza-lasat? A valasz igen egyszeru: indirekt fuggvenyhıvast kell alkalmaznunk,

Page 55: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

50 5. FEJEZET. FEJLETTEBB TECHNIKAK

azaz a menupont leıro strukturaban egy fuggvenyre mutato pointermezotkell deklaralnunk. Az egyes menupontok definialasakor ezt a strukturame-zot a tenylegesen meghıvando fuggveny cımevel kell majd inicializalnunk.

A konkret deklaraciok

Most tekintsuk tehat az egyes tıpusdeklaraciokat! A menurendszerunk ku-lonbozo menukbol all, a kulonbozo menuk pedig tobb menupontbol. Egymenupont legfontosabb jellemzoje az a fuggveny, amit a menupont kiva-lasztasakor aktivizalni kell. Ezek a fuggvenyek igen sokfelek lehetnek, ıgyhagyomanyos C-ben celszeru a fuggvenyek cımeit nyilvantartani. Ehhezket lepcsoben definialjuk a fad (function address) tıpust:

typedef int intfunc(int);/* int-et visszaado, 1 int-et varo ** fuggvenytipus */

typedef intfunc *fad; /* intfunc tipusu tarolasi egy- ** segre mutato tipus */

A fent definialt intfunc tıpust felhasznalhatjuk a majdan meghıvandoegyes fuggvenyek elozetes deklaralasara.

A vegrehajtando fuggvenyen kıvul egy menupont fontos jellemzoje az il-leto menupont neve (azonosıto szovege), az a nyomtathato karakter, amivelEnter megnyomasa helyett kivalaszthato a menupont. Celszeru megenged-nunk, hogy a menupont altal meghıvando fuggvenynek egy, a menupontleırasaban tarolt parametert is atadjunk. Ha a menurendszerunkhoz alkal-mas help-rendszert is szeretnenk, celszeru az egyes menupontokhoz rendelthelp-szovegre utalo informaciot (peldaul egy file-indexet) is tarolni. Eze-ket az informaiokat – a menuponthoz rendelt fuggveny cımevel egyutt – azalabb deklaralt menuitem (menupont) strukturaba szerveztuk:

typedef struct{

char *text; /* A menupont azonosito szovege */char key; /* A menupontot kivalaszto betu */int helpindex; /* A menuponthoz rendelt help-kod */fad function; /* A menuponthoz tartozo fv-re mutat */int param; /* A ’*function’ fuggveny parametere */

} menuitem;

A figyeljuk meg, hogy a fenti struktura definicobol kimaradt a tıpuscımke,hiszen typedef-fel eleve azonosıtot rendelunk hozza – rekurzıv adatdefini-corol pedig szo sincs.

Page 56: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 51

Most lassuk, hogy szervezhetunk egy menut a fenti modon deklaraltmenuitem strukturak segıtsegevel.

A menupontjainkat celszeruen egy menuitem tıpusu tombben taroljuk,amelynek meretet is tudnunk kell. A menu tartalma mellett fontos annakmegjelenese is. Szuksegunk lehet arra, hogy a menut keretezo doboz tete-jen esetleg egy menunevet, egy fejlecet (header-t) is megjelenıtsunk. Fontosazt is tudnunk, hogy melyik x-y karakterpozicioba kerul a menudoboz (an-nak peldaul a bal felso sarka) a kepernyon, es az is lenyeges informacio,hogy hany karakterpoziciot foglal le a menudoboz vızszintes es fuggolegesiranyban. Azt is nyilvantarthatjuk egy menurol, hogy melyik menupontotvalasztottuk ki benne utoljara es fontos lehet az is, hogy az adott menu holhelyezkedik el egy hierarchikus menu-fan. Ezeket az informaciokat foglaltukegybe az alabbi menutype strukturaban:

typedef struct{

char *header; /* A menu fejlecszovegere mutat */int x; /* A menudoboz bal felso sarkanak */int y; /* x es y koordinatai, valamint */int xs; /* a menudoboz x es */int ys; /* y iranyu merete. */int itemno; /* A menupontok szama */menuitem *items; /* A menupontok listajara mutat. */int hierarch; /* Ha 1, kozvetlenul a fomenu hivja */int lastitem; /* Utoljara kivalasztott pont szama */

} menutype;

A menuitem tıpusbol egy-egy inicializalt tombot szervezve hozhatjuk letreaz egyes menuk tartalmara vonatkozo adathalmazt. Egy ilyen lista kez-docıme kerul egy menutype struktura items mezojebe. Egy-egy menutypestruktura egy komplett menu leırasat tartalmazza. Ezekbol a strukturak-bol szinten egy tombot szervezunk, ez lesz a menus tomb. E tomb elsonehany eleme egy-egy menufa gyokeret (azaz a fomenu egyes pontjaikentaktivizalando menuket) reprezentalja, a tobbi elem pedig az egyes fakra fel-fuzott almenuket ırja le. Tekintsuk at tehat a teljes menurendszert definialoadatstrukturat:

/* Kulso fuggvenyek deklaracioja */

extern intfunc data, r_data, w_data, statf,regr, linf, barf,save, load;

Page 57: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

52 5. FEJEZET. FEJLETTEBB TECHNIKAK

/* A a menukezelo fuggveny prototipus erteku deklaracioja */

intfunc menu; /* El\-ore hivatkozashoz */intfunc dir, shell; /* Tovabbi fv-ek el\-ore hivatkozshoz */

/* Az egyes menulistak (items_0 .. items_3) es a menuk: */

menuitem items_0[ ] ={ /* text key hlp func. param. */

"Directory", ’D’, 1, dir, 0,"Os shell", ’O’, 2, shell, 0,"File", ’F’, 3, menu, 3,/*a 3.sz. menu almenu lesz */exitxt, ’X’,-1, NULL, 0 /*-1-es parameter: exit */

}; /* Tombmeret: */#define N0 sizeof(items_0)/sizeof(menuitem)

menuitem items_1[ ] ={

"Default", ’D’, 4, data, 7,"Read data", ’R’, 5, r_data,1,"List data", ’L’, 6, w_data,2,"Statistics",’S’, 7, statf, 3,exitxt, ’X’,-1, NULL, 0

};#define N1 sizeof(items_1)/sizeof(menuitem)

menuitem items_2[ ] ={

"Regression",’R’, 8, regr, 4,"Plot", ’P’, 9, linf, 5,"Bar", ’B’,10, barf, 6,exitxt, ’X’,-1, NULL, 0

};#define N2 sizeof(items_2)/sizeof(menuitem)

menuitem items_3[ ] ={

"Save", ’S’,11, savef, 0,"Load", ’L’,12, loadf, 0,exitxt, ’X’,-1, NULL, 0

};#define N3 sizeof(items_3)/sizeof(menuitem)

Page 58: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 53

/* A teljes menurendszer leirasa: */menutype menus[ ] ={/* head. x y xs ys itemno items hier. last */

"", 9, 2, 13, N0+3, N0, items_0, 1, 0,"", 35, 2, 14, N1+3, N1, items_1, 1, 0,"", 61, 2, 14, N2+3, N2, items_2, 1, 0,"Files",11, 6, 8, N3+3, N3, items_3, 0, 1

};

Figyeljuk meg, hogy a menulistak meretenek meghatarozasat a fordıto prog-ramra bıztuk: a sizeof operator segıtsegevel megkapjuk mind az egyesmenulistakat tartalmazo tombok helyfoglalasat byte-okban, mind a menu-item tıpus meretet; ezek hanyadosa adja meg a menulista tombok logikaimeretet (azaz azt, hogy hany elemu egy menulista). Ezeket a kifejezeseket#define makrokent definialjuk, es az ıgy kapott kifejezeseket hasznaljukfel a menus tomb inicializalasara. Ez egy igen flexibilis megoldas, ugyanisegy menulista bovıtese soran a menus tomb inicializalasakor a menudobozmeretere es a menulista hosszara vonatkozoan automatikusan helyes adatotfog a fordıto felhasznalni. A menus tomb kitolteset legfeljebb csak akkorkell modosıtani, ha egy uj menulista-elem hossza nagyobb, mint az adottmenudobozhoz megadott xs ertek.

Sajat include file-ok

Menurendszerunk egy fuggvenyekre mutato pointerekbol allo tomb segıt-segevel aktivizalja az egyes menupontokhoz rendelt fuggvenyeket. Ahhoz,hogy ezt a pointertombot ki lehessen tolteni, szukseg van a sajat fuggvenye-ink prototıpusaira. Fontos, hogy csak int tıpust visszaado, egyetlen inttıpusu parametert varo fuggvenyeket illeszthetunk be a menurendszerbe.Ha ettol eltero rutinjaink vannak, akkor azokat ”fejeljuk meg” ugy, hogyennek a kovetelmenynek eleget tegyenek. Ezeket a fuggvenyeket vagy ugydeklaraljuk, ahogy azt az adatstruktura leırasakor tettuk, vagy egy includefile-ba foglajuk a deklaraciokat. A ketto egyszerre is alkalmazhato, felteve,ha a ketfele deklaracio osszhangban all egymassal. Mi most a menukezelorendszerben torteno deklaraciot alkalmazzuk, es csak a menukezelo rutinokdeklaracioit helyezzuk el a sajat file-ban.

A bevezetoben emlıtett, nem portabilis kepernyokezelo fuggvenyeinketegy onallo .c file-ban erdemes tarolni, prototıpusaikat szinten a fuggvenyrpototıpusokat tartalmazo include file-unkban erdemes elhelyezni.

Ezt az include file-t, amit peldaul myfunc.h-nak nevezhetunk – majd amenukezelo rendszert tartalmazo .c file fogja behıvni a

Page 59: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

54 5. FEJEZET. FEJLETTEBB TECHNIKAK

#include "myfunc.h"

preprocesszor utasıtassal.Erdemes a menukezelo rendszerunk altal hasznalt kulonfele szimbolu-

mokat is – peldaul egyes specialis billentyuk kodjainak szimbolikus neveit,mint peldaul RIGHT ami a → billentyu kodjanak, LEFT, UP, DOWN, ESC, BE-GIN, END, HELP rendre a ←, ↑, ↓, Esc, Enter, Home, End es az F1 billentyukodjanak felel meg az IBM PC-n – egy szimbolum file-ba foglalni. Legyenennek a file-nak a neve peldaul mysymb.h. Ezt a file-t szinten az #includedirektıvaval epıthetjuk be a rendszer minden egyes .c file-jaba. (Megje-gyezzuk, hogy ez a file akar #define-nal deklaralt makroszeru konstansokattartalmazhat, akar const-kent definialt konstansok deklaracioit tartalmaz-hatja – az itt kozolt programreszletek szempontjabol ez lenyegtelen. Egymasik megjegyzes az egyes billentyukhoz rendelt kodokra vonatkozik: Aspecialis billentyukhoz celszeru 128-nal nagyobb kodokat rendelni. Igy abillentyuzet kezelo fuggveny altal visszadott billentyukodok kozul konnyenkiszurhetok a kozvetlen ASCII karakterkodok. A menukezelo rendszerbenezzel a feltetelezessel elunk.

Page 60: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 55

5.3.2. A menukezelo rendszer listaja

/************************************************************* File: menu.c ** Tartalom: Menukezelo mintaprogram **************************************************************/

#include <stdio.h> /* Standard i/o csomag */#include <string.h> /* Sztring- es memoriakezelo rutinok */#include <stdlib.h> /* Altalanos celu standard fuggvenyek */#include <ctype.h> /* Karakterkezelo makrok */

#include "myfunc.h" /* Sajat fuggvenyek prototipusai */#include "mysymb.h" /* Szimbolumok (spec. billentyuk kodjai)*/

/* ======================================================== *//* Tipusdeklaraciok */

typedef int intfunc(int);/* int-et visszaado, 1 int-et varo ** fuggvenytipus */

typedef intfunc *fad; /* intfunc tipusu tarolasi egy- ** segre mutato tipus */

typedef struct{

char *text; /* A menupont azonosito szovege */char key; /* A menupontot kivalaszto betu */int helpindex; /* A menuponthoz rendelt help-kod */fad function; /* A menuponthoz tartozo fv-re mutat */int param; /* A ’*function’ fuggveny parametere */

} menuitem;

typedef struct{

char *header; /* A menu fejlecszovegere mutat */int x; /* A menudoboz bal felso sarkanak */int y; /* x es y koordinatai, valamint */int xs; /* a menudoboz x es */int ys; /* y iranyu merete. */int itemno; /* A menupontok szama */menuitem *items; /* A menupontok listajara mutat. */

Page 61: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

56 5. FEJEZET. FEJLETTEBB TECHNIKAK

int hierarch; /* Ha 1, kozvetlenul a fomenu hivja */int lastitem; /* Utoljara kivalasztott pont szama */

} menutype;

/* ======================================================== *//* Tarolasi egysegek deklaracioi, definicioi: */

static char exitxt[~] = "eXit";/* Majd sokszor kell ez a sztring. */

/* Kulso fuggvenyek deklaracioja */

extern intfunc data, r_data, w_data, statf,regr, linf, barf,save, load;

/* A a menukezelo fuggveny prototipus erteku deklaracioja */

intfunc menu; /* El\-ore hivatkozashoz */intfunc dir, shell; /* Tovabbi fv-ek el\-ore hivatkozshoz */

/* Az egyes menulistak (items_0 .. items_3) es a menuk: */

menuitem items_0[ ] ={ /* text key hlp func. param. */

"Directory", ’D’, 1, dir, 0,"Os shell", ’O’, 2, shell, 0,"File", ’F’, 3, menu, 3,/*a 3.sz. menu almenu lesz */exitxt, ’X’,-1, NULL, 0 /*-1-es parameter: exit */

}; /* Tombmeret: */#define N0 sizeof(items_0)/sizeof(menuitem)

menuitem items_1[ ] ={

"Default", ’D’, 4, data, 7,"Read data", ’R’, 5, r_data,1,"List data", ’L’, 6, w_data,2,"Statistics",’S’, 7, statf, 3,exitxt, ’X’,-1, NULL, 0

};#define N1 sizeof(items_1)/sizeof(menuitem)

Page 62: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 57

menuitem items_2[ ] ={

"Regression",’R’, 8, regr, 4,"Plot", ’P’, 9, linf, 5,"Bar", ’B’,10, barf, 6,exitxt, ’X’,-1, NULL, 0

};#define N2 sizeof(items_2)/sizeof(menuitem)

menuitem items_3[ ] ={

"Save", ’S’,11, savef, 0,"Load", ’L’,12, loadf, 0,exitxt, ’X’,-1, NULL, 0

};#define N3 sizeof(items_3)/sizeof(menuitem)

/* A teljes menurendszer leirasa: */menutype menus[ ] ={/* head. x y xs ys itemno items hier. last */

"", 9, 2, 13, N0+3, N0, items_0, 1, 0,"", 35, 2, 14, N1+3, N1, items_1, 1, 0,"", 61, 2, 14, N2+3, N2, items_2, 1, 0,"Files",11, 6, 8, N3+3, N3, items_3, 0, 1

};

/*Mivel a fomenunek semmi mas funkcioja nincs, mint a menu fuggvenynekatadni a vezerlest a megfelelo menuindexszel, komolyabb adatstrukturakatnem definialtunk a szamara. Csak az alabbiakra van szukseg a fomenuhoz:*/

static char main_header[ ] = /* A fomenu fejlecszovege */" Highly Portable Menu System ";

static char options[ ]=/*Az egyes menuk kivalaszto gombjai */"FDP"; /*Sorrendjuk ugyan\-az, mint az alab- */

/*bi sztring-tomb el\-emeinek sorrendje*/

/*Az options sztring hossza adja meg, hogy a menus tomb hanyadik elemeigtekintjuk a menuket a fomenu reszeinek.*/

Page 63: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

58 5. FEJEZET. FEJLETTEBB TECHNIKAK

static char *headers[ ]= { "File", /* A fomenube felvett */"Data", /* menuk fejlec szove- */"Plot" /* gei. */

};static int mainselect = 0; /* Az utoljara kiv.fomenu elem */static char buffer[81]; /* Ide generaljuk a fomenut */static int xp,yp,j; /* Segedvaltozok a rutinokhoz */static char inpbuff[256]; /* Altalanos input buffer */

/*A magyarazatok es deklaraciok utan kovetkezzenek maguk a fuggvenyek!A menukezelo rendszert ugy hoztuk letre, hogy programunk main-je csakilyen rovid legyen:*/

/************************************************************/void main(void) /* Ez tetszoleges program ’main’-je lehet *//************************************************************/{

displ_ini(); /* A kepernyokezelo rendszer inicializalasa */main_frame();/* Keretrajzolas a fomenuhoz: mint az IDE */main_menu(0);/* Fomenu. Addig fut, amig ESC-pel

ki nem szallnak belole */displ_end(); /* A kepernyo alapallapotanak

helyreallitasa */exit(0); /* OK hibakod visszadasa az operacios

rendszernek */}

/* Most tekintsuk magat a menukezelo rutincsomagot! */

/************************************************************/int menu(int index)/*Az aktualisan hasznalando menu indexe *//*

Funkcio:A fuggveny a menus[index]-ben adott menut megjelenıti a kepernyon. Azegyes menupontokat a menuleıras szerinti dobozban jelenıti meg. A me-nus[index].lastitem indexu menupont kiemelve latszik a kepen. A ki-emelt menupontot a ↑ es ↓ kurzorvezerlokkel valtoztathatjuk. Ha leut-juk az Enter billentyut, akkor a kiemelt szinu menupont fuggvenyet hiv-juk meg, ha pedig valamelyik menuponthoz rendelt nagybetut utjuk le abillentyuzeten, akkor az illeto menupont fuggvenye lesz aktivizalva a me-nus[index].items[selected].param parameterrel, ahol index a kivalasz-tott menupont indexe. Amint a meghıvott fuggveny visszaadja a vezerlest,

Page 64: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 59

a menu szubrutin regeneralja az aktualis menulistat a keretezett dobozban.Ha menus[index].hierarch == 1 akkor a menu fuggveny visszateresi er-teke

– RIGHT ha a → kurzorvezerlo gombot nyomtak meg,

– LEFT ha a ← kurzorvezerlo gombot nyomtak meg.

Minden egyeb esetben a visszateresi ertek 0, tehat amikor

– az ESC gombot nyomtak meg (kilepes a menu fuggvenybol),

– olyan menupontot valasztottak ki, amelynek a helpindex-e -1

*************************************************************/{

int i, /* A menupontok szamat tesszuk bele */l, /* for-ciklushoz ciklusvaltozo */exit, /* Kilepest jelzo flag */par, /* A kivalasztott fv. parametere */cmd; /* A vezerlo-karakternek */

/* .......... E L O K E S Z I T E S E K ............... */

j = menus[index].lastitem;/* j-ben az aktualis index */i = menus[index].itemno;if (!i) return 0; /* Nulla meretu menuvel nem torodunk */

menu_regen(index,1);/* A menut kiiratjuk a kepernyore */exit = FALSE; /* A kilepest jelzo flag kezdoerteke */

/* .............. F O C I K L U S ................... */

while (! exit) /*Addig tart,amig exit igaz nem lesz */{

cmd = 0; /* Kezdetben ures parancs */while (!(cmd == SELECT || cmd == CR)){

cmd = getkey(); /* VT100-on ketfele ENTER van, */switch(cmd) /* ezert van CR is es SELECT is. */{

case BEGIN:o_gotoxy(xp,yp+j); /* HOME-ot nyomott */printf("%s",menus[index].items[j].text);

Page 65: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

60 5. FEJEZET. FEJLETTEBB TECHNIKAK

j = 0;o_gotoxy(xp,yp);highlight(EMPHAS,menus[index].items[j].text);break;

case END:o_gotoxy(xp,yp+j); /* END-et nyomott */printf("%s",menus[index].items[j].text);j = i-1;o_gotoxy(xp,yp+j);highlight(EMPHAS,menus[index].items[j].text);break;

case UP: /* ’fel’ nyil */{

o_gotoxy(xp,yp+j);printf("%s",menus[index].items[j].text);if (j > 0) j--; else j = i - 1;o_gotoxy(xp,yp+j);highlight(EMPHAS,menus[index].items[j].text);

}break;

case DOWN: /* ’le’ nyil */{

o_gotoxy(xp,yp+j);printf("%s",menus[index].items[j].text);if (j < i-1) j++; else j = 0;o_gotoxy(xp,yp+j);highlight(EMPHAS,menus[index].items[j].text);

}break;

case HELP: /* F1-et nyomtak */menus[index].lastitem = j;

menu_help(menus[index].items[j].helpindex);if (menus[index].items[j].helpindex >= 0 &&

menus[index].y + menus[index].ys > 11)menu_regen(index,0);break;

case ESC: /* ESC-et nyomtak */exit = 1;cmd = SELECT;break;

case LEFT:case RIGHT:

Page 66: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 61

/* Ha ’main_menu’ hivta ’menu’-t, akkor a’jobbra’, ’balra’ nyilak eseten a menut to-roljuk, es a nyil-gomb kodjat visszaadjuk.Igy a fomenu a roll-in menut felvaltja egymasikkal: */

if (menus[index].hierarch == 1){

menu_remove(index);return cmd;

}default:

/* Kilepunk, ha dedikalt gombot nyomtak */if (cmd < 128){

cmd = toupper(cmd);for(l = 0; l < i; l++){

if (menus[index].items[l].key == cmd){

o_gotoxy(xp,yp+j);printf("%s",menus[index].items[j].text);cmd = SELECT;j = l;break;

}}

}break;

} /* ............... end switch .................. */} /* ................. end while .................... */if (! exit){

exit = (menus[index].items[j].helpindex == -1);}

/*Ezen a ponton mar eldolt, hogy ki akarunk-e lepni. Ha nem, akkor viszonttudjuk, hogy melyik menupont fuggvenyet kell aktivizalni:*/

if (! exit){ /* Az ’eXit’ pontnak mindig -1 helpindexe legyen! */

Page 67: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

62 5. FEJEZET. FEJLETTEBB TECHNIKAK

/* .... A kivalasztott fuggveny aktivizalasa:.... *//* (j indexeli a kivalasztott fuggvenyt) */

o_gotoxy(xp,yp+j);highlight(EMPHAS,menus[index].items[j].text);menus[index].lastitem = j;par = menus[index].items[j].param

(*menus[index].items[j].function)(par);

menu_regen(index,0); /* A menu-box regeneralasa */}else{

menu_remove(index);}

}return 0;

}/************************************************************/void menu_regen(int index, /* A regeneralando menu indexe */

int rem) /* TRUE: torolni kell a dobozt */

/*Funkcio:A menus[index] menu regeneralasa (ujra rajzolja a dobozt, kiırja a menu-listat es kiemelo szinnel nyomtatja az utoljara kivalasztott menupontot.)Ha rem == 1 akkor a menu altal elfoglalt kepernyoteruletet torli a menu-lista kiırasa elott, ha rem == 0, akkor nem torol.

*************************************************************/{

int i,k,l,m,n,xx,yy;int x1,x2;xp = menus[index].x; /* Pozicio, meret el\-ovetele */yp = menus[index].y;i = menus[index].itemno;xx = menus[index].xs;yy = menus[index].ys;

/* Dobozrajzolas */box_draw(menus[index].header,xp,yp,xx,yy,rem);

xp += 2;

Page 68: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 63

yp += 2;for (k = 0; k < i; k++) /* A menulista megjelenitese */{

o_gotoxy(xp,yp+k);if (k == menus[index].lastitem){

highlight(EMPHAS,menus[index].items[k].text);j = k;

}else

printf("%s",menus[index].items[k].text);}

}/************************************************************/void menu_remove(int index) /* A torlendo menu indexe */

/*Funkcio:A menus[index] menu torlese a kepernyorol

*************************************************************/{

int xx,yy,x1,y1;x1 = menus[index].x;y1 = menus[index].y;xx = menus[index].xs;yy = menus[index].ys;box_delete(x1,y1,xx,yy);

}/************************************************************/void box_draw(char* header, /* ->a doboz fejlec-szevege */

int xp, int yp,/* a doboz pozicioja, */int xs, int ys,/* merete */int rem) /* 1, ha torles kell, egyebkent 0 */

/*Funkcio:Egy xs, ys meretu dobozt rajzol az xp, yp pozicioba. A keret felso reszenekkozepere a header fejlecet ırja ki. Ha rem == 1, akkor a doboz rajzolasaelott torli a doboz altal elfoglalando teruletet.

*************************************************************/{

Page 69: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

64 5. FEJEZET. FEJLETTEBB TECHNIKAK

int l,n,xx,yy;int x1,x2;l = strlen(header); /* A fejlec hossza */xx = xs-2; /* Egyeb adatok el\-okeszitese */x1 = (xx - l)/2;x2 = xx - (x1 + l);yy = ys-2;if (rem) box_delete(xp,yp,xs,ys);

o_gotoxy(xp,yp); /* A legfelso sor a fejleccel */printf("%c",UPLEFT);for (n = 0; n < x1; n++) printf("%c",HORIZ);

highlight(REVERSE|BRIGHT,header);

for (n = 0; n < x2; n++) printf("%c",HORIZ);printf("%c",UPRIGHT);yp++;for (n = 0; n < yy; n++) /* Maga a doboz */{

o_gotoxy(xp,yp+n);printf("%c",VERT);o_gotoxy(xp+1+xx,yp+n);printf("%c",VERT);

}o_gotoxy(xp,yp+yy); /* A doboz legalso sora */printf("%c",DOWNLEFT);for (n = 0; n < xx; n++) printf("%c",HORIZ);printf("%c",DOWNRIGHT);

}/************************************************************/void box_delete(int xp, int yp, /* Egy dobozt torol */

int xs, int ys) /* Pozicio, meret */

/*Funkcio:Egy xs, ys meretu dobozt torol az xp, yp poziciorol.

*************************************************************/{

int n, m;

Page 70: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 65

for (n = ys-1; n >= 0; n--){

o_gotoxy(xp,yp+n);for (m = 0; m < xs; m++) putc(’ ’);

}}

/************************************************************/void menu_help(int index) /* A menupont help-indexe */

/*Funkcio:Az index altal meghatarozott help-szoveget kikeresi egy help-file-bol, eskiırja a kepernyore. A kiırashoz egy 7 soros ablakot nyit, a szoveget 7 so-ronkent ırja ki. Ha van meg kiirando szoveg, akkor a More ... uzenet utanegy billentyuleutesre var, ha nincs, akkor a Press any key ... uzenet utantorli a kepernyorol a help-dobozt, es visszater. A help-file formatuma akovetkezo:

index_i n_isor_1_isor_2_i...

sor_n_iindex_j n_j...

ahol index i az i-edik help-index, n i az ehhez az indexhez tertozo help-szoveg sorainak a szama, valamint sor 1 i, ... sor n i a help-szoveg egyessorai. Formatum hiba, vagy file vege eseten szinten hibajelzes tortenik.

*************************************************************/{

static char hunex[] = "Unexpected end of the help-file!!!";#define YP 24

FILE *fp;int i,j,k,err;

if (index < 0) return; /* Negativra visszater */

box_draw(" HELP ",2,11,76,9,1);/* Help-box rajzolasa */

Page 71: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

66 5. FEJEZET. FEJLETTEBB TECHNIKAK

fp = fopen(helpdat,"r"); /* Help-file megnyitasa */

if (fp == NULL) /* Ha nem letezik a file, hibajelzes */{

o_gotoxy((80-(21+strlen(helpdat)))/2-1,16);printf("Help-file %s not found!",helpdat);goto helpend1;

}i = -1;while (i != index) /* Help-index keresese a file-ban */{

err = fscanf(fp,"%d%d",&i,&j);if (err == EOF) /* Nem talaljuk: hibajelzes */{

o_gotoxy(19,16);printf("No help is available for this menu item!");goto helpend0;

}if (err != 2){

o_gotoxy((79-(31+strlen(helpdat)))/2,16);printf("Format error in the %s help-file!",helpdat);goto helpend0;

}if (i != index)/* Ha meg nem talalja, tovabb olvas. */{

for (; j >= 0; j--){

if (NULL == fgets(inpbuff,74,fp)){

o_gotoxy((79-strlen(hunex))/2,16);printf(hunex);goto helpend0; /* A ’goto’-t legfeljebb igy */

} /* hasznaljuk! */}

}}for (k = i = 0; i < j; i++){

if (NULL == fgets(inpbuff,74,fp)){

o_gotoxy((79-strlen(hunex))/2,16);

Page 72: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 67

printf(hunex);goto helpend0;

}o_gotoxy(4,12+k);printf(inpbuff);k++;if (k == 7)/*Megvan a helpszoveg. 7-esevel kiirjuk: */{

o_gotoxy(66,YP);highlight(BRIGHT,"More ...");bell();err = getkey();o_gotoxy(66,YP);printf(" ");if (err == ESC)/* ESC-re kiszallunk */{

fclose(fp);box_delete(2,11,76,9);return;

}box_draw(" HELP ",2,11,76,9,1);k = 0;

}}helpend0: /* Minden befejezeskor ezeket a muveleteket */fclose(fp); /* kell elvegezni, tehat takarekos meg- */helpend1: /* oldas a ’goto’ hasznalat. Csinjan ban- */press_key();/* junk az ilyennel, hogy olvashato marad- */box_delete(2,11,76,9); /* jon a programunk! */

}

/************************************************************/void main_frame(void)

/*Funkcio:Keretet rajzol a fomenunek. Ha valamelyik fuggveny torli az egesz keper-nyot, akkor main frame meghıvasaval helyreallıthatja azt.

*************************************************************/{

erase();box_draw(main_header,0,0,80,23,0);/* Main box fejleccel */

Page 73: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

68 5. FEJEZET. FEJLETTEBB TECHNIKAK

main_menu(1); /* Fomenu statusz-sora */}/************************************************************/void main_menu(int stl)/*Ha 1, akkor a statusz-sort kiirja */

/*Funkcio:A menukezelo rendszer fo rutinja, ezt kell a main-bol meghıvni. A me-nus tombbol annyi menut kezel kozvetlenul, amennyi az options sztringhossza. A menu-opciokat a kepernyo masodik, kivilagıtott soraban jelenıtimeg. Egy menupont a szokasos modon valaszthato (kurzorral kiemeles,majd Enter, vagy a kezdo betu leutese). Ha egy almenu el (azaz latszik akepernyon), akkor a ←, illetve → nyilakkal a szomszedos menure valtha-tunk.

*************************************************************/{

int i,j,k,l,posinc,hno,xp,cmd,flag;

/* ’buffer’-ben lesz az inverzen kiirando statusz-sor */

hno = sizeof(headers)/sizeof(char*);posinc = 78/hno;

xp = posinc/2;

if (stl){

for (i = 0; i < 78; buffer[i++] = ’ ’);for (j = 0; j < hno; j++){

l = strlen(headers[j]);for(k = 0; k < l; k++)

buffer[xp+j*posinc+k] = *(headers[j]+k);}buffer[78] = ’\0’;o_gotoxy(1,1);highlight(REVERSE,buffer);

Page 74: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 69

}

/* A kivalasztott menut normal modon jelenitjuk meg: */

i = mainselect;xp++;if (stl){

o_gotoxy(xp+i*posinc,1);printf(headers[i]);return;

}

/* A fo parancs-ciklus. Csak ESC-re lephetunk ki belole. */

dontquit: /* Ide ugrunk, ha megse lepunk ki. */flag = cmd = 0;while (cmd != ESC){ if (! flag) cmd = getkey();

flag = 0;switch (cmd) /* Nincs el\-o almenu. Kurzorvezerlok */{ /* feldogozasa, status-sor modositasa */

case RIGHT:o_gotoxy(xp+i*posinc,1);highlight(REVERSE,headers[i]);if (i < hno-1) i++; else i = 0;o_gotoxy(xp+i*posinc,1);printf(headers[i]);break;

case LEFT:o_gotoxy(xp+i*posinc,1);highlight(REVERSE,headers[i]);if (i) i--; else i = hno-1;o_gotoxy(xp+i*posinc,1);printf(headers[i]);break;

case SELECT:crselect:

/* Kivalasztottak egy almenut. Megje- *//* gyezzuk indexet ’mainselect’-ben */

mainselect = i;

Page 75: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

70 5. FEJEZET. FEJLETTEBB TECHNIKAK

flag = menu(i);/* A ’menu’ rutin behivasa */switch (flag) /* Mi a visszateresi ertek? */{

case RIGHT: /* Stat.sor modositas ... */o_gotoxy(xp+i*posinc,1);highlight(REVERSE,headers[i]);if (i < hno-1) i++; else i = 0;o_gotoxy(xp+i*posinc,1);printf(headers[i]);break;

case LEFT:o_gotoxy(xp+i*posinc,1);highlight(REVERSE,headers[i]);if (i) i--; else i = hno-1;o_gotoxy(xp+i*posinc,1);printf(headers[i]);break;

}l = strlen(headers[i]);o_gotoxy(xp+i*posinc+l,1);break;

default:if (cmd < 128) /*Kezdobetuvel valasztottak */{

cmd = toupper(cmd);for (l = 0; l < hno; l++)if (cmd == options[l]){

o_gotoxy(xp+i*posinc,1);highlight(REVERSE,headers[i]);i = mainselect = l;o_gotoxy(xp+i*posinc,1);printf(headers[i]);

/* Ugy teszunk, mintha ’nyil+Enter’-relvalasztottak volna. */

cmd = SELECT;goto crselect;

}}break;

}

Page 76: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 71

}

/* Az ESC-pel valo kilepes szandekat meg\-erosittetjuk: */

box_draw("",28,5,24,3,0);o_gotoxy(30,6);highlight(BRIGHT,"Are you sure? (y/n) ");cmd = yesno();box_delete(28,5,24,3);o_gotoxy(1,1);if (!cmd) goto dontquit; /*Nem lep ki, vissza az elejere */erase();

}*************************************************************/

/*Ket gyakori funkcio portabilis megvalosıtasat talaljuk itt. Ezek az aktıvkonyvtar tartalmanak kiiratasa a kepernyore, illetve az operacios rendszerparancs-ertelmezo burkanak (command shell) az aktivizalasa. Mindkettota system fuggveny segıtsegevel oldjuk meg. A system argumentuma egyoperacios rendszernek szolo parancsot tartalmazo sztring. Ezek a mi ese-tunkben egy-egy #define makrokent lettek megadva, ıgy azok operaciosrendszertol fuggo felteteles fordıtassal megfeleloen beallıthatok. Tahat asystem fuggvenynek (process.h) atadando operacios endszer parancsok:*/

#ifdef __MSDOS__#define DIRSTR "dir /w/p"

/* A burok (shell) ’dir’ parancsa */#define SHELL "COMMAND.COM"

/* Maga az operacios r. burok (shell) */#endif

/*A fenti sztringeket csak a DOS-ban adhatjuk at a system-nek, ezert hasz-naltuk az #ifdef MSDOS fordıtasvezerlo direktıvat. UNIX-ban a meg-felelo sztringek erteke rendre "ls -C|more", illetve "sh" lenne.*/

/************************************************************/int dir(int d) /* Az aktiv konyvtar tartalmat nezzuk meg */

/* d: Dummy parameter */

Page 77: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

72 5. FEJEZET. FEJLETTEBB TECHNIKAK

/************************************************************/{

displ_end();system(DIRSTR); /* Az op. rendszer DIR parancsat kerjuk.

Ennel lehetne hatekonyabb meg\-oldastis talalni, de ez igy portabilis. */

displ_ini();press_key();erase();main_frame();return d;

}/************************************************************/int shell(int d)/* A parancsertelmezo burok hivasa */

/* d: Dummy parameter *//************************************************************/{

displ_end();printf("\nType exit to return!\n\n");system(SHELL);displ_ini();erase();main_frame();return(x);

}

Page 78: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

5.3. OSSZETETT MINTAPELDA 73

A sajat include file tartalma

A kovetkezo lista a myfunc.h include file javasolt tartalmat mutatja be:

/************************************************************* File: myfunc.h ** Tartalom: Menukezelo mintaprogram fuggveny proto- ** tipusai: el\-orehivatkozasokhoz, illetve a ** kepernyokezelo rendszer hasznalatahoz. **************************************************************/

/* ======================================================== *//* A menukezelo rendszer fuggvenyeinek deklaracioi */

void main_menu(int stl); /* Fomenu-rutin. Ezt kell a main-bol meghivni */

void main_frame(void); /* A fomenuhoz keretet rajzol akepernyore */

int menu(int index); /* Ez a menurutin: az adott sor-szamu menut kezeli */

void menu_regen(int index); /* Az adott sorszamu menut rege-neralja a kepen */

void menu_remove(int index);/* Az adott sorszamu menut letor-li a keprol */

void menu_help(int index); /* Adott menuponthoz helpet ir kiegy file-bol */

void box_draw(char *header, /* Adott fejleccel, */int xp,int yp,/* adott xp,yp poxicioban, */int xs,int ys,/* adott xs,ys meretben dobozt */int rem); /* rajzol, ha kell, torol alatta*/

void box_delete(int xp, /* Adott helyrol adott meretu */int yp, /* dobozt torol */int xs,

int ys);/* ======================================================== *//* A keprnyokezelo rutinok prototipusai magyarazatokkal */

void o_gotoxy(int x, int y); /* Sajat pozicionalo.x=0..24, y=0..79 */

void home(void); /* A kurzort a 0,0 poziciobahelyezi */

void erase(void); /* Torli a kepernyot es a0,0-ba pozicional */

Page 79: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

74 5. FEJEZET. FEJLETTEBB TECHNIKAK

void displ_ini(void); /* Bekapcsolja a kepernyo-kezelo rendszert, torol */

void displ_end(void); /* Kikapcsolja a kepernyo-kezelo rendszert, torol */

void cursor_left(int n); /* A kurzort egy poziciovalbalra viszi */

void cursor_right(int n); /* A kurzort egy poziciovaljobbra viszi */

void cursor_up(int n); /* A kurzort egy poziciovalfeljebb helyezi */

void cursor_down(int n); /* A kurzort egy poziciovallejebb helyezi */

void highlight(unsigned mode,/* ’mode’ szerinti attributum-char* string);/* mal ’string’-et nyomtatja */

int read_in(char* string); /* Egy sztringet olvas be */

int yesno(void); /* y/Y/I/i/N/n (igen/nem)valaszt var. 0, ha ’nem’ */

int input(char* string,int pos,int len);/* Sor-editor. Adott pozici-

on, adott hosszt edital */void press_key(void); /* A ’Press a key’ kiirasa u-

tan gombnyomasra var */void bell(void); /* Egy BEL karaktert kuld az

stdout-ra: igy beep-el */int getkey(void); /* A billentyuzetet kezeli,

ASCII-t, illetve #defineerteket ad vissza (pl. UP) */

Page 80: Poppe-Kocsis - C programozási feladatgyűjtemény (1992, 80 oldal)

Irodalomjegyzek

[1] Benko Tiborne – Urban Zoltan. Az IBM PC programozasa Turbo C 2.0nyelven. BME Mernoki Tovabbkepzo Intezete, 1990. Jegyzet.

[2] Benko Tiborne – Poppe Andras – Benko Laszlo. Bevezetes a BORLANDC++ programozsba. Computer Books, 1991.

[3] B. W. Kernighan – D. M. Ritchie. A C programozasi nyelv. MuszakiKonyvkiado., 1985. Forditotta Dr. Siegler Andras.

[4] B. W. Kernighan – D. M. Ritchie. The C Programming Language.Prentice Hall, 1988. Second Edition.

75