Professional Documents
Culture Documents
feladatgyűjtemény
Előszó iii
1. C alapok 1
1.1. Ciklusok . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 1
1.1.1. Feladat: Fahrenheit-Celsius átszámoló program . . . 1
1.1.2. Feladat: Fahrenheit-Celsius átszámoló for ciklussal 3
1.2. Egykarakteres I/O . . . . . . . . . . . . . . . . . . . . . . . 5
1.2.1. Feladat: Karakterszámlás . . . . . . . . . . . . . . . 6
1.3. Elágazás: if, switch . . . . . . . . . . . . . . . . . . . . . . 7
1.3.1. Feladat: Sorszámlálás . . . . . . . . . . . . . . . . . 7
1.4. Önálló feladatok . . . . . . . . . . . . . . . . . . . . . . . . 8
1.4.1. Feladat: Blank-ek számlálása . . . . . . . . . . . . . 8
1.4.2. Feladat: Blank-ek számlálása tı́pusonként . . . . . . 8
1.5. Az operátorok gyakorlása . . . . . . . . . . . . . . . . . . . 8
1.5.1. Feladat: A sizeof egyszerű használta . . . . . . . . 8
1.5.2. Feladat: A sizeof és 2-vel való szorzás a shifteléssel 8
1.5.3. Feladatok: Aritmetikai műveletek int-ekkel . . . . . 8
2. Bonyolultabb szerkezetek 11
2.1. Az előfeldolgozó használata . . . . . . . . . . . . . . . . . . 11
2.1.1. Szimbólumok használata, feltételes fordı́tás . . . . . 12
2.1.2. Feladat: Programfordı́tás körülményeinek kiiratása . 13
2.1.3. Feladat: Az #ifdef alkalmazása . . . . . . . . . . . 14
2.1.4. Fordı́tási időben élő szimbólumok . . . . . . . . . . . 15
2.1.5. Új és régi stı́lusú függvénydeklarácók . . . . . . . . . 15
2.1.6. Feladat: . . . . . . . . . . . . . . . . . . . . . . . . . 17
2.1.7. Makródefiniciók . . . . . . . . . . . . . . . . . . . . . 17
2.1.8. Feladatok: min, max, pow, toupper, tolower . . . . 18
2.2. Tömb-, pointer- és függvénytı́pusok . . . . . . . . . . . . . . 19
2.3. Karaktertömbök és pointerek . . . . . . . . . . . . . . . . . 23
i
ii
5. Fejlettebb technikák 43
5.1. Struktúrák – láncolt lista . . . . . . . . . . . . . . . . . . . 43
5.1.1. Fealadat: Láncolt lista készı́tése . . . . . . . . . . . 43
5.2. Menürendszer . . . . . . . . . . . . . . . . . . . . . . . . . . 46
5.3. Összetett mintapélda . . . . . . . . . . . . . . . . . . . . . . 47
5.3.1. A tervezés egyes fázisai . . . . . . . . . . . . . . . . 47
5.3.2. A menükezelő rendszer listája . . . . . . . . . . . . . 55
Irodalomjegyzék 75
Előszó
iii
iv Előszó
Köszönetnyilvánı́tás
Szeretnénk megköszönni Benkő Tibornénak azt, hogy fáradhatatlanul bı́z-
tatott minket arra, hogy a BME Villamosmérnöki Karának nappali tago-
zatos 1. évfolyamos hallgatóinak oktatása során összegyűjtött tapasztala-
tainkat jelen példatár összeállı́tásával közzé tegyük.
Köszönet illeti Verhás Pétert is, aki HION nevű programját rendelkezé-
sünkre bocsátotta. Ez a program tette lehetővé, hogy a magyar nyelvű,
ékezetes szövegfile-jainkat gond nélkül használhassuk a TEXszövegformázó
rendszer LATEXmakrócsomagjával.
C alapok
Jelen fejezet célja az, hogy megéreztesse a C programozás ı́zét. Mivel a tel-
jesen portábilis programozási stı́lust igyekszünk bemutatni, az itt ismertett
példák, illetve a kitűzött feladatok akár TURBO C-vel, akár BORLAND
C++-szal, akár VAX C-vel lefordı́thatók. Lehetőség szerint ANSI C fordı́-
tási opciót használjunk, ha IBM PC-n dolgozunk.
A továbbiakban feltételezzük, hogy az olvasó alaposan ismer már egy
programozási nyelvet, például a Pascal-t. Először egyszerű példákat köz-
lünk mind Pascal, mind C nyelven – részben csak egyszerű szintaxis vál-
tással, részben kihasználva a C nyújtotta tömörı́tési lehetőségeket, majd
egyes problémáknak a C nyelvű megvalósı́tását közöljük, végül pedig csak
feladatkiı́rásokat adunk meg, a C nyelvet tanulókra bı́zva az egyes felada-
tok konkrét, C nyelvű megvalósı́tását. E fejezet feldolgozásához javasolt
olvasmány [2]-ból: 2.1, 2.2, 2.4.4, 2.4.5, 2.5, 2.7 fejezetek.
1.1. Ciklusok
1.1.1. Feladat: Fahrenheit-Celsius átszámoló program
Készı́tsünk olyan programot, amely egy adott tartományon belül, adott
lépésközzel kilistázza a Fahrenheit fokokban adott hőmérséklet Celsius fo-
kokban számolt értékét.
1
2 1. FEJEZET. C ALAPOK
A Pascal változat:
PROGRAM FAHRANHEIT(INPUT, OUTPUT);
VAR FAHR, CELS: INTEGER;
LOWER, UPPER, STEP: INTEGER;
BEGIN
LOWER:=0;
UPPER:=300;
STEP:=20;
FAHR:=LOWER;
WRITELN;
WHILE(FAHR <= UPPER) DO
BEGIN
CELS:=5*(FAHR-32) DIV 9;
WRITELN(FAHR,’ ’,CELS);
FAHR:=FAHR+STEP;
END;
END.
A C változat:
#include <stdio.h>
main()
{
int fahr, cels;
int lower, upper, step;
lower = 0;
upper = 300;
step = 20;
fahr = lower;
printf("\n");
while(fahr <= upper)
{
1.1. CIKLUSOK 3
Megoldás:
#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);
}
}
További lehetőség:
Az inicializálást áthelyezzük a deklarációs részbe, azaz inicializált változó-
kat hozunk létre. Ekkor a for ciklus inicializáló része egy üres utası́tás
lesz:
#include <stdio.h>
main()
{
int fahr = 0, cels;
4 1. FEJEZET. C ALAPOK
printf("\n");
for ( ; /* initialization - empty statement */
fahr <= upper; /* condition */
fahr += step) /* stepping */
{
cels = 5 * (fahr- 32) / 8;
printf("%d\t%d\n",fahr,cels);
}
}
Szimbólumok használata:
#include <stdio.h>
#define LOWER 0
#define UPPER 300
#define STEP 20
main()
{
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);
}
}
1.2. EGYKARAKTERES I/O 5
Ennek C megfelelője:
#include <stdio.h>
main()
{
int ch; /* int and char are compatible */
ch = getchar();
while (ch != EOF)
{
putchar(ch);
ch = getchar();
}
}
Továbbfejlesztés:
#include <stdio.h>
main()
{
int ch; /* int and char are compatible */
#include <stdio.h>
main()
{
int n = 0;
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 tömörı́tsünk egy kicsit ezen a programon! Kihasználva azt, hogy a
relációs operátorok ligikai értéket szolgáltatnak, ami vagy 1 (ha igaz a re-
láció), vagy 0 (ha nem igaz), a következőképpen ı́rhatjuk át programunkat:
#include <stdio.h>
main()
{
int n, nl;
int ch;
n = nl = 0;
while ((ch = getchar()) != EOF)
{
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);
}
c) eldönti egy egész számról, hogy tökéletes szám-e (Egy tökéletes szám
eggyel nagyobb, mint az összes valódi osztójának az összege.),
Bonyolultabb szerkezetek
11
12 2. FEJEZET. BONYOLULTABB SZERKEZETEK
C fordı́tóprogram
forrásszöveg - előfeldolgozó - belső fordı́tó - tárgykód
2.2. táblázat.
A Borland C++-ban és a VAX C-ben definiált, az operációs rendszerre,
illetve a fordı́tóra utaló szimbólumok
A megoldás:
/***** File: test.c ******/
#include <stdio.h>
int sor = 0;
main()
{
#if defined(__STDC__) || VAXC
sor = __LINE__;
/*
* 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
2.1. AZ ELŐFELDOLGOZÓ HASZNÁLATA 17
double mypower(
#ifdef PROTOTYPES
double, double
#endif
);
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:
Írjunk egy olyan C nyelvű faktoriális-számı́tó programot, amelyben a fel-
használóval való kommunikációt a main végzi, és a faktoriális értékét egy
saját függvény hı́vásával végzi! A faktoriális számı́tó függvény ne végezzen
I/O műveletet! Úgy ı́rjuk meg a függvényeink (main, faktoriális számı́tó)
definicióit, hogy a programunk mind régi stı́lusú C fordı́tóval, mind pedig
prototı́pust igénylő fordı́tóval lefordı́tható legyen! Alkalmazzuk a korábban
leı́rtakat! A fordı́táshoz a Unix cc-t, illetve a C++ üzemmódba kapcsolt
Borland C++-t használjuk!
2.1.7. Makródefiniciók
A #define direktı́vát nemcsak fordı́tásvezérlésre, illetve szimbólikus kons-
tansok definiálásra használhatjuk, hanem paraméterekkel rendelkező ún.
makrók, azaz egyszerű rutinok ı́rására is. Például a math.h szabványos
fejlécfile-ban definiált abs rutin makró megvalósı́tása ı́gy néz ki:
#define abs((x)) ((x) > 0 ? (x) : (-(x)))
18 2. FEJEZET. BONYOLULTABB SZERKEZETEK
/************************************************************
* 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.... */
2.2. TÖMB-, POINTER- ÉS FÜGGVÉNYTÍPUSOK 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 ......... */
Módosı́tsuk tehát ezt a példaprogramot úgy, hogy a szabványos makrók
helyett a saját makróinkat használják a karakterkonverzióra.
2.3. táblázat.
Tı́pusmódosı́tó operátorok. A precedencia felülről lefelé csökken.
azaz egy függvényt deklarál. dfunc ”egy double-t visszadó, egy int pa-
ramétert váró” tı́pusú függvény azonosı́tója. (Vegyük észre a különbséget
a definició és a deklaráció között: a definició létre is hozza a tárolási egy-
séget, mı́g a deklaráció csak azt mondja meg, hogy milyen tı́pusú az illető
tárolási egység – egy függvény teljesértékű megadásához a függvénytörzsre
is szükség lenne.)
Egy származtatott tı́pus megadásának a logikája a következő: megad-
juk a definiálandó/deklarálandó tárolási egység alaptı́pusát (ez itt most a
double), majd megadjuk a tárolási egység azonosı́tóját, és hozzákapcsolunk
egy ún. tı́pusmódosı́tó operátort. Természetesen egy adott tárolási egység
azonosı́tójához nemcsak egy tı́pusmódosı́tó operátor kapcsolható, hanem
több is:
Tehát a fenti példában megnevezett új tı́pus az ip. Vegyük észre, hogy
a fenti tı́pusdeklaráció olyan, mintha ip ”typedef” tárolási osztályú int
tı́pusú változó lenne. Persze nem az, hanem csak az int tı́pussal megegye-
ző értelmű újabb tı́pus azonosı́tója. A változódeklarációval analóg logikát
folytatva, alakı́tsuk át ip értelmezését. Legyen ip például egy int-re mu-
tatú pointer tı́pusának az azonosı́tója:
Megjegyzések:
1. A C-ben a tömbök indexelése mindig 0-tól kezdődik. A C-ben nincs
automatikus indexhatár ellenőrzés.
2. Egy tömbváltozó azonosı́tója önmagában, mint kifejezés, a tömb kez-
dőcı́mét jelenti, tehát alaptı́pus* tı́pusú kifejezés. Például a
Művelet Eredmény
pointer + int pointer
pointer - int pointer
pointer - pointer int
1. megoldás:
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];
}
szemlélteti a
2. megoldás:
3. megoldás:
4. megoldás:
2.4. Függvénypointerek
Függvényekre mutató pointerekre sok esetben szükségünk lehet. Gondol-
junk csak arra, hogy egy numerikus intgerálást végző rutinnak tetszőleges
integrálandó függvény esetében működnie kell, lehetőleg változtatás nélkül.
Nos, erre a C kiváló lehetőségeket nyújt. Mielőtt azonban egy univerzális
integráló rutint ı́rnánk, teküntsünk egy egyszerűbb példát.
Felhasználás: qsort
Tegyük fel, hogy egy adott tı́pusú adathalmazt valamilyen szempont szerint
rendeznünk kell. Legyen ez az adathalmaz mondjuk egy tömbben adott.
Felmerülhet bennünk, hogy az első félév során megismert valamelyik rende-
ző algoritmust mi magunk lekódoljuk C-ben, és ezzel a probléma meg is van
oldva. Nos, ez egy járható út, de két ellenvetésünk is lehet. Az egyik az,
hogy az adatrendezésre sok előre elkészı́tett rutin létezik, úgyhogy nagy va-
lószinűséggel időt és munkát pazarlunk a saját próbálkozásunkkal. A másik
ellenvetés az lehet, hogy ha mégis nekilátunk egy rendező rutin ı́rásának,
nagy valószinűséggel az általunk elkészı́tett változat túlságosan testresza-
bott lesz, azt később nehézkes lesz más programokban felhasználni.
Az igzság az, hogy az adatrendezést igen egyszerűen megoldhatjuk az
stdlib.h szabványos fejlécfile-ban deklarált qsort függvény felhasználásá-
val. Ez a rutin az ismert quicksort (gyorsrendező) algoritmussal dolgozik.
Prototı́pusa a következőképpen néz ki:
void qsort(void *base, size_t nelem, size_t width,
int (*fcmp)
(const void *elem1, const void *elem2));
Értelmezzük az egyes paramétereket! Az első paraméter, base a rendezendő
tömb kezdőcı́me. Mivel tetszőleges tı́pusú adatok jöhetnek szóba, base-t
’általános pointertı́pusunak’ (void*) deklarálták. Majd a függvényhı́vás
során nekünk kell az ún tı́pusátalakı́tó (type cast) operátorral a mi mutató-
tı́pusunkat void* tı́pusúvá alakı́tanunk. A második paraméter (nelem) és a
harmadik paraméter (width) tı́pusa size t. Ez egy szabványos tı́pusjelölés.
Lényegében ez egy int, de ez az alternatı́v név arra hı́vja fel a programozó
26 2. FEJEZET. BONYOLULTABB SZERKEZETEK
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Indirekt függvényhı́vás
Láttuk, hogy a függvénypointerek használata nagy flexibilitást tud kölcsö-
nözni kész rutinok számára. Ezt használjuk ki arra, hogy egy integráló
függvényt ı́rjunk a félév elején megismert valamelyik numerikus integráló
algoritmus felhasználásával. Az integráló függvény deklarációja a következő
legyen:
double integration(double a, double b, int n,
double (*f)(double x));
Írjunk olyan főprogramot, amely egy-két ismertebb függvényosztályba tar-
tozó függvény integrálját számolja ki! A szükséges főprogram egy lehetséges
megvalósı́tását a következő oldalon találhatjuk. Tekintsük át alaposan ezt
28 2. FEJEZET. BONYOLULTABB SZERKEZETEK
#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 */
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;
A dinamikus tárkezelés
alapjai
#include <stdlib.h>
33
34 3. FEJEZET. A DINAMIKUS TÁRKEZELÉS ALAPJAI
#include <stdlib.h>
...
int n;
double *tombmut;
...
printf("Size="); scanf("%d",&n); putchar(’\n’);
Segı́tség a megoldáshoz:
Célszerű egy már létező programot (akár Pascal, akár C) átı́rni, illetve mó-
dosı́tani. A vektorok számára a helyfoglalást a bevezető isemertető alapján
könnyen megvalósı́thatjuk. Gondot csak a 2 dimenziós együttható mátrix
jelenthet. Tegyük föl, hogy statikus helyfoglalás esetében a mátrix dekla-
rációja a következő:
#define N 3
double amat[N][N];
Az amat tömböt úgy is felfoghatjuk, mintha az alábbi módon lenne dekla-
rálva:
36 3. FEJEZET. A DINAMIKUS TÁRKEZELÉS ALAPJAI
#define N 3
double sor0[N],
sor1[N],
sor2[N];
double *amat[] = { sor0, /* ez mind double* tipusu */
sor1,
sor2
};
azaz amat nem más, mint double* tı́pusú pointerek tömbje. Itt még min-
den statikus – az egyes sorok mértét explicit módon definiáltuk, mı́g amat
méretét implicit módon, az inicializáló kifejezés adja meg. Látjuk tehát,
hogy amat egy double-ra mutató pointerek tömbjének kezdőcime, maga is
egy – igaz, konstans – pointer, olyan, mintha double** tı́pusúnak deklarál-
tuk volna. Ennek alapján felı́rhatjuk most már a dinamikus helyfoglalásra
alkalmas deklarációt is:
double** amat; /* Sehova nem mutat, de majd fog! */
Maga a dinamikus helyfoglalás két részből rakható össze. Először az amat
nevű, n elemű (n futási időben megadott egész paraméter – az aktuális
ismeretlenek száma) duplapontosságú valós számokra mutató pointerek tá-
rolásár szolgáló tömböt hozzuk létre az amat = ((double*)*) malloc(n
* sizeof(double*)); utasással, majd minden egyes sor számára fogla-
lunk helyet. Például az i-edik sort az amat[i] = (double*) malloc(n *
sizeof(double)); utası́tással hozhatjuk létre.
A mátrix által elfoglalt memóriaterület felszabadı́tásakor fordı́tott sor-
rendet kell követnünk. Előszor az egyes sorokat szűntetjük meg, majd
magát az amat tömböt.
Fontos figyelmeztetések:
1. Attól, hogy egy pointert deklaráltunk, még nem lesz értéke, ı́gy seho-
va sem mutat!
2. Attól, hogy egy pointernek van értéke, azaz mutat valahová, még min-
dig nem biztos, hogy érvényes memória-területre mutat. Azt a me-
móriaterületet, ahová egy pointerrel mutatni szeretnénk, LE KELL
FOGLALNI!
3. A C-ben az indexelés 0-tól indul, és tömbméret - 1-ig tart. A tömbtúl-
cı́mzés miatt nem szól a fordı́tó, legfeljebb elszáll a program. Komo-
lyabb operációs rendszerekben (VMS, UNIX) maga az operációs rend-
szer figyelmeztet arra, hogy érvénytelen memóricı́mre hivatkozunk.
3.1. DINAMIKUS ADATOK 37
Az operációs rendszerrel
való kapcsolat
#include <stdio.h>
main()
{
int ch; /* int and char are compatible */
ch = getchar();
while (ch != EOF)
{
putchar(ch);
ch = getchar();
}
}
Most már tudjuk, hogy itt egyszerűen arról van szó, hogy az stdin előre
definiált folyamból az stdout előre definiált folyamra másolunk. Ezek a
folyamok – hacsak az operációs rendszer szintjén át nem irányı́tottuk őket
– a billentyűzethez, illetve a terminálképernyőhöz vannak hozzárendelve.
39
404. FEJEZET. AZ OPERÁCIÓS RENDSZERREL VALÓ KAPCSOLAT
main(argc, argv)
int argc;
char *argv[];
myprog:=="myprog.exe"
Fejlettebb technikák
43
44 5. FEJEZET. FEJLETTEBB TECHNIKÁK
...
/* 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
5.1. STRUKTÚRÁK – LÁNCOLT LISTA 45
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 */
...
}
5.2. Menürendszer
Portabilitási megfontolások
Ha fáradtságos munkával megtervezünk és létrehozunk egy, a fenti kı́vánal-
maknak megfelelő felhasználói felületet, célszerű azt úgy programozni, hogy
ne csak IBM-PC kompatibilis számı́tógépeken, a DOS operációs rendszer
alatt, BORLAND C++ fordı́tóval lefordı́tva fusson, hanem jól körülhatá-
rolt módosı́tások után bármely, C fordı́tóval rendelkező géptı́puson, bár-
mely operációs rendszeren (pl. VT100-as terminálokkal rendelkező VAX
48 5. FEJEZET. FEJLETTEBB TECHNIKÁK
A konkrét deklarációk
Most tekintsük tehát az egyes tı́pusdeklarációkat! A menürendszerünk kü-
lönböző menükből áll, a különböző menük pedig több menüpontból. Egy
menüpont legfontosabb jellemzője az a függvény, amit a menüpont kivá-
lasztásakor aktivizálni kell. Ezek a függvények igen sokfélék lehetnek, ı́gy
hagyományos C-ben célszerű a függvények cı́meit nyilvántartani. Ehhez
két lépcsőben definiáljuk a fad (function address) tı́pust:
typedef int intfunc(int);/* int-et visszaado, 1 int-et varo *
* fuggvenytipus */
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 figyeljük meg, hogy a fenti struktúra definicóból kimaradt a tı́puscı́mke,
hiszen typedef-fel eleve azonosı́tót rendelünk hozzá – rekurzı́v adatdefini-
córól pedig szó sincs.
5.3. ÖSSZETETT MINTAPÉLDA 51
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;
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)
5.3. ÖSSZETETT MINTAPÉLDA 53
#include "myfunc.h"
preprocesszor utası́tással.
Érdemes a menükezelő rendszerünk által használt különféle szimbólu-
mokat is – például egyes speciális billentyűk kódjainak szimbólikus neveit,
mint például RIGHT ami a → billentyű kódjának, LEFT, UP, DOWN, ESC, BE-
GIN, END, HELP rendre a ←, ↑, ↓, Esc, Enter, Home, End és az F1 billentyű
kódjának felel meg az IBM PC-n – egy szimbólum file-ba foglalni. Legyen
ennek a file-nak a neve például mysymb.h. Ezt a file-t szintén az #include
direktı́vával épı́thetjük be a rendszer minden egyes .c file-jába. (Megje-
gyezzük, hogy ez a file akár #define-nal deklarált makrószerű konstansokat
tartalmazhat, akár const-ként definiált konstansok deklarációit tartalmaz-
hatja – az itt közölt programrészletek szempontjából ez lényegtelen. Egy
másik megjegyzés az egyes billentyűkhöz rendelt kódokra vonatkozik: A
speciális billentyűkhöz célszerű 128-nál nagyobb kódokat rendelni. Így a
billentyűzet kezelő függvény által visszadott billentyűkódok közül könnyen
kiszűrhetők a közvetlen ASCII karakterkódok. A menükezelő rendszerben
ezzel a feltételezéssel élünk.
5.3. ÖSSZETETT MINTAPÉLDA 55
/* ======================================================== */
/* Tipusdeklaraciok */
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. */
56 5. FEJEZET. FEJLETTEBB TECHNIKÁK
/* ======================================================== */
/* Tarolasi egysegek deklaracioi, definicioi: */
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)
5.3. ÖSSZETETT MINTAPÉLDA 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)
/*
Mivel a főmenünek semmi más funkciója nincs, mint a menu függvénynek
átadni a vezérlést a megfelelő menüindexszel, komolyabb adatstruktúrákat
nem definiáltunk a számára. Csak az alábbiakra van szükség a főmenühöz:
*/
static char main_header[ ] = /* A fomenu fejlecszovege */
" Highly Portable Menu System ";
*************************************************************/
{
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 ............... */
/* .............. F O C I K L U S ................... */
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:
5.3. ÖSSZETETT MINTAPÉLDA 61
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);
xp += 2;
5.3. ÖSSZETETT MINTAPÉLDA 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 */
/*
Funkció:
A menus[index] menü törlése a képernyőről
*************************************************************/
{
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 */
/*
Funkció:
Egy xs, ys méretű dobozt rajzol az xp, yp pozicióba. A keret felső részének
közepére a header fejlécet ı́rja ki. Ha rem == 1, akkor a doboz rajzolása
előtt törli a doboz által elfoglalandó területet.
*************************************************************/
{
64 5. FEJEZET. FEJLETTEBB TECHNIKÁK
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);
highlight(REVERSE|BRIGHT,header);
}
/************************************************************/
void box_delete(int xp, int yp, /* Egy dobozt torol */
int xs, int ys) /* Pozicio, meret */
/*
Funkció:
Egy xs, ys méretű dobozt töröl az xp, yp pozicióról.
*************************************************************/
{
int n, m;
5.3. ÖSSZETETT MINTAPÉLDA 65
/************************************************************/
void menu_help(int index) /* A menupont help-indexe */
/*
Funkció:
Az index által meghatározott help-szöveget kikeresi egy help-file-ból, és
kiı́rja a képernyőre. A kiı́ráshoz egy 7 soros ablakot nyit, a szöveget 7 so-
ronként ı́rja ki. Ha van még kiirandó szöveg, akkor a More ... üzenet után
egy billentyűleütésre vár, ha nincs, akkor a Press any key ... üzenet után
törli a képernyőről a help-dobozt, és visszatér. A help-file formátuma a
következő:
index_i n_i
sor_1_i
sor_2_i
...
sor_n_i
index_j n_j
...
*************************************************************/
{
FILE *fp;
int i,j,k,err;
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)
/*
Funkció:
Keretet rajzol a főmenünek. Ha valamelyik függvény törli az egész képer-
nyőt, akkor main frame meghı́vásával helyreállı́thatja azt.
*************************************************************/
{
erase();
box_draw(main_header,0,0,80,23,0);/* Main box fejleccel */
68 5. FEJEZET. FEJLETTEBB TECHNIKÁK
int i,j,k,l,
posinc,
hno,xp,
cmd,flag;
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);
5.3. ÖSSZETETT MINTAPÉLDA 69
i = mainselect;
xp++;
if (stl)
{
o_gotoxy(xp+i*posinc,1);
printf(headers[i]);
return;
}
mainselect = i;
70 5. FEJEZET. FEJLETTEBB TECHNIKÁK
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();
}
*************************************************************/
/*
Két gyakori funkció portábilis megvalósı́tását találjuk itt. Ezek az aktı́v
könyvtár tartalmának kiiratása a képernyőre, illetve az operációs rendszer
parancs-értelmező burkának (command shell) az aktivizálása. Mindkettőt
a system függvény segı́tségével oldjuk meg. A system argumentuma egy
operációs rendszernek szóló parancsot tartalmazó sztring. Ezek a mi ese-
tünkben egy-egy #define makróként lettek megadva, ı́gy azok operációs
rendszertől függő feltételes fordı́tással megfelelően beállı́thatók. Tahát a
system függvénynek (process.h) átadandó operációs 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 át a system-nek, ezért hasz-
náltuk az #ifdef MSDOS fordı́tásvezérlő direktı́vát. UNIX-ban a meg-
felelő sztringek értéke rendre "ls -C|more", illetve "sh" lenne.
*/
/************************************************************/
int dir(int d) /* Az aktiv konyvtar tartalmat nezzuk meg */
/* d: Dummy parameter */
72 5. FEJEZET. FEJLETTEBB TECHNIKÁK
/************************************************************/
{
displ_end();
system(DIRSTR); /* Az op. rendszer DIR parancsat kerjuk.
Ennel lehetne hatekonyabb meg\-oldast
is 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);
}
5.3. ÖSSZETETT MINTAPÉLDA 73
/* ======================================================== */
/* A menukezelo rendszer fuggvenyeinek deklaracioi */
75