Programozás C nyelven – Wikikönyvek
Ugrás a tartalomhoz
A Wikikönyvekből, a szabad elektronikus könyvtárból.
Az első C program
szerkesztés
Tekintsük a következő egyszerű C nyelven írt programot:
#include
int
main
()
printf
" Első C programom
\n
);
return
A program kimenete az idézőjelek között szereplő felirat, a kurzor pedig a következő soron áll.
Nézzünk néhány megjegyzést a fenti programmal kapcsolatban:
A C programozási nyelv különbséget tesz a kis és a nagy betűk között. Minden C parancsnak kis betűsnek kell lennie.
A C program belépési pontját a
main
()
függvényhívás azonosítja. Egyelőre a függvényt argumentum nélkül hívtuk meg, a későbbiekben ennek részletezésére még kitérünk.
és
a kezdő és a végpontját jelölik a végrehajtási résznek.
#include
nélkül nem működne a
printf
()
függvény.
printf
()
-ben a kiírandó szöveget dupla idézőjelek közé kell tennünk. Vegyük észre azt is, hogy a \n karakter nem került kiírásra. Tehát a
printf
()
külön tartalmazza a kiírandó szöveget és a kiírást befolyásoló változókat. Ami a dupla idézőjelek között megjelenik változtatás nélkül kiírásra kerül, kivétel ez alól a \ és % karaktereket követő jel, vagy a jelsorozat. A \n változó a fordító számára azt jelenti, hogy a következő karakter kiírása előtt új sort kell kezdenie.
A parancsokat a
zárja le.
Megjegyzések
szerkesztés
A megjegyzéseket (kommenteket) a programszöveg magyarázatainak beírására szoktuk használni. Hibakereséskor is jó hasznát vehetjük. Ha a program egy részének végrehajtását szeretnénk kihagyni, akkor azt megjegyzés blokkba zárhatjuk.
// ez most egy egysoros megjegyzés
/* ez most
többsoros
megjegyzés */
A változókról
szerkesztés
A C típusos programnyelv. Ez azt jelenti, hogy mielőtt egy változót használni szeretnénk, deklarálnunk kell azt. Figyeljünk arra, hogy a fordítóprogram a változónevekben is különbséget tesz a kis és a nagy betűk között. A névre a következő megkötések érvényesek:
csak betűket, számjegyeket és aláhúzás karaktert tartalmazhat
betűvel kell kezdődnie
hossza legfeljebb 32 karakter (implementációfüggő)
A változódeklaráció szabálya C-ben a következő:
típus név1, név2, …;
Például:
int
float
Egy változót kezdőértékkel is elláthatunk.
int
10
Nézzük a következő C programot:
#include
int
main
()
int
sum
sum
500
15
printf
"500 és 15 összege %d
\n
sum
);
return
A programban létrehoztunk egy egész típusú változót, majd egy összeget rendeltünk hozzá. Figyeljük meg a változó kiíratását! Az idézőjelek között most találkozunk először a % jellel. Mögötte kell megadnunk, hogy erre a helyre milyen típusú változó kerül kiírásra. A
betű az egész típusra utal. Az idézőjel után következik a változó neve. Ennek értéke kerül majd beírásra a szövegbe.
Az adatoknak a C-ben négy alaptípusa van: egész (
int
), karakter (
char
), lebegőpontos (
float
double
).
Az alaptípusokat elláthatjuk módosító jelzőkkel is, ekkor az értékkészlet módosul.
Pl.
int
unsigned
int
Az első esetben az
értéke –32768 és 32767 között lehet, míg a második esetben a
értéke 0 és 65535 közötti szám lehet. A
signed
módosító jelző is használható, de alapértelmezés szerint minden egész változó ilyen lesz.
A következő táblázatban felsoroltunk néhány típust, értékkészletükkel együtt.
Adattípus
Értékkészlet
Méret (byte)
Pontosság (tizedesjegy)
char
-128..127
(egész)
unsigned char
0..255
int
-32768..32767
unsigned int
0..65535
long int
-2147483648..2147483647
unsigned long int
0..4294967295
float
3.4e-38..3.8e+38
double
1.7e-308..1.7e+308
15
long double
3.4e-4932..3.4e+4932
10
19
printf
()
függvényben a változókat csak úgy tudjuk kiíratni, hogy az idézőjelek között % jel után megadjuk a változó típusát, a kiírás formátumát majd az idézőjelek után vesszőkkel elválasztva fölsoroljuk a változók neveit. A gyakrabban használt karaktereket a következő táblázatban soroltuk föl:
%d
decimális egész
%u
előjel nélküli decimális egész
%f
lebegőpontos
%c
karakter
%s
string vagy karaktertömb
%e
dupla lebegőpontos
Lehetőség van arra is, hogy meghatározzuk a változó értéke által elfoglalt mező szélességét. Nézzük a következő példákat:
int
v1
printf
"…%5d"
v1
);
A v1 egész változó 5 karakter helyen jelenik meg.
float
f1
printf
"…%5.2f"
f1
);
Az f valós változót 5 mezőre írja ki 2 tizedes pontossággal.
int
v1
10
printf
"…%*d"
v1
);
A * jelentése ebben az esetben az, hogy a mezőszélességet az idézőjel utáni első változó határozza meg. Tehát a fenti példában a v1 egész változó 10 szélességű mezőre kerül kiírásra.
Ha egy változó karakter típusú, akkor értékét egyszeres idézőjelek között kell megadnunk.
char
betu
betu
'A'
Egész típusú változónak adhatunk 16-os vagy 8-as számrendszerbeli értéket is.
int
okt
hex
okt
0567
hex
0x2ab4
hex
0X2AB4
Minden változó esetén figyeljünk a kezdőérték megadására. Ha ezt nem tesszük meg, a változónak akkor is lesz kezdőértéke, de biztosan nem olyan, amit mi szerettünk volna adni.
Felhasználói típus
szerkesztés
Ha egy típus túl hosszú és gyakran ugyanazt kell használnunk a program során, akkor érdemes egy szinonimával, típusnévvel hivatkozni rá. Ennek a módja:
typedef
típus
típusnév
Egy konkrét példán keresztül
typedef
unsigned
long
int
egesz
egesz
Néhány további formázási lehetőség a
printf
()
-ben:
\n sortörés, soremelés, új sor
\t tabulátor
\r kocsi vissza
\f lapdobás
\v függőleges tabulátor
\a csengő
3.,4.,5.,6. feladat
Konstansok
szerkesztés
A konstansok megadásának két módját ismertetjük az alábbiakban.
A const kulcsszó használatával
szerkesztés
const
int
30
Ebben az esetben vigyázni kell arra, hogy a konstanst inicializáljuk is. Hibajelzést ad a következő deklaráció:
const
int
30
Az így létrehozott konstansok értéke közvetlenül nem változtatható meg. A konstansok azonban a memóriában tárolódnak, így értékük közvetetten mutatók használatával módosítható.
Makróval
szerkesztés
Az előfordítónak különböző definíciókat (helyettesítéseket), leírásokat (preprocesszor-utasításokat) adhatunk, erről a későbbiekben még részletesen lesz szó. Most egyetlen példát nézzünk a konstansok megadására egyszerű makróval.
#define ADO_KULCS 0.25
Az így megadott konstansok a program listájának elején szerepelnek az
#include
beillesztések után. Szintaktikailag a
a sor első karaktere kell hogy legyen, az ilyen sorokat nem zárhatjuk pontosvesszővel, és minden sorban csak egy
#define
állhat. Mivel ezek a leírások az előfordítónak szólnak, ezért minden olyan helyen, ahol a programlistában az
ADO_KULCS
azonosító szerepel, az előfordító
0.25
karaktereket fog beírni. Ezért az így létrehozott konstansok értéke még indirekt módon sem változtatható.
Operátorok
szerkesztés
A programok írása során gyakran van szükségünk kifejezések felépítésére, váltózónak történő értékadásra, számolási műveletekre, függvényhívásokra. A C nyelvben ezek a kifejezések operandusok, függvényhívások és operátorok kombinációjából épülnek fel.
Az operátorokat többféle szempont szerint lehet csoportosítani.
Az operandusok száma szerint (egy, kettő, három operandus)
Az operátor típusa szerint (aritmetikai, logikai, léptető, bitművelet, értékadó, feltételes)
Az operátor helye szerint (prefix, postfix)
Itt az operátorokat a típusuk szerint tárgyaljuk, de említést teszünk a másik két szempontról is.
Aritmetikai operátorok
szerkesztés
A négy alapművelet, a szokásos szimbólumokkal, valamint a maradékos osztás
jellel. A művelet az osztás maradékát adja vissza, természetesen csak egész típusú változók használata esetén alkalmazható. A másik négy művelet mind egész, mind lebegőpontos operandusok esetén is működik. Vigyázzunk a következőhöz hasonló esetekben.
int
12
float
A programrészlet után az f értéke a 2.000000 lebegőpontos érték lesz. Tehát az eredmény típusát az operandusok típusa döntötte el.
A műveletek sorrendje a matematikában megszokott.
Összehasonlító és logikai operátorok
szerkesztés
Feltételekben és ciklusokban gyakran kell összehasonlítani különböző értékeket, ennek elvégzésére a hasonlító operátorokat használjuk. Ezek a következők:
<, >, <=, >=, ==, !=
. Ha ezekkel két változót vagy kifejezést hasonlítunk össze, akkor az eredmény int típusú lesz, és értéke 1, ha a reláció igaz, illetve 0, ha hamis.
A logikai kifejezésekben gyakran összetett feltételeket is meg kell fogalmazni, erre szolgálnak a logikai operátorok. Ezek a következők:
a tagadás művelete, egyoperandusú.
&&
logikai és,
||
logikai vagy műveletek. A műveletek precedenciája a táblázatban.
Léptető operátorok
szerkesztés
A változó értékének eggyel való növelésére, vagy csökkentésére szolgálnak. Egyoperandusú műveletek. Postfix és prefix alakban is írhatók.
++
eggyel növeli,
--
eggyel csökkenti a változó értékét. Ha egy kifejezésben csak egy változó szerepel, akkor mindegy, hogy postfixes, vagy prefixes alakját használjuk az operátoroknak. (
a++
egyenértékű a
++a
-val) Ha azonban egy kifejezés kiértékelésében használjuk, akkor már óvatosabban kell bánni a két alakkal.
int
=++
++
A programrészletben az első értékadás előtt az
értéke 1-gyel nő, 5 lesz, ezt kapja az
változó, tehát annak értéke is 5 lesz, a második értékadásban az
megkapja a pillanatnyi értékét az ötöt, majd az
értéke 1-gyel nő, azaz 6 lesz. Az operátorok mind egész, mind lebegőpontos operátorokkal működnek.
C-ben nem szokás az
a=a+1;
értékadás, helyette minden esetben a léptetést használjuk.
Bitműveletek
szerkesztés
A műveletek operandusai csak
char
short
int
és
long
típusú előjel nélküli egészek lehetnek.
A műveletek első csoportja két operandusú.
1-es komplemens,
bitenkénti ÉS,
bitenkénti VAGY,
bitenkénti KIZÁRÓ VAGY. Ezeket a műveleteket leggyakrabban maszkolásra, bitek törlésére vagy beírására szoktuk használni. A KIZÁRÓ VAGY érdekes tulajdonsága, hogy ha ugyanazt a maszkot kétszer alkalmazzuk egy értékre, akkor visszakapjuk az eredeti értéket.
A műveletek másik csoportjába a biteltoló műveletek tartoznak.
<<
eltolás balra,
>>
eltolás jobbra. A felszabaduló pozíciókba 0 kerül, a kilépő bitek elvesznek. A műveletek két operandusúak.
a<<2
az
változó bitjeit 2-vel tolja balra. Nyilvánvalóan az n bittel való balra tolás 2
-nel (kettő n-edik hatványra emelve) való szorzást, míg az n bittel való jobbra tolás 2
-nel való egész osztást eredményez.
Értékadó operátorok
szerkesztés
Az értékadás történhet a más nyelvekben megszokottak szerint.
a=érték;
vagy
a=kifejezés;
formában
Van azonban olyan forma is, mely a hagyományos nyelvektől teljesen idegen.
Ebben az esetben az
és a
változó is kap értéket. Az értékadás operátor mindig jobbról balra értékelődik ki, tehát a kiértékelés után a fenti kifejezésben
értéke 4,
értéke pedig 13 lesz. A kiértékelés ilyen sorrendje miatt van az, hogy a C-ben az összetett értékadás is működik.
a=b=c=0;
értékadás után mindhárom változó értéke 0 lesz.
Van az értékadásnak C-ben egy tömörebb formája is. Általános alakban a következőképpen írható le:
változó
változó
op
kifejezés
helyett a
változó
op
kifejezés
Ez a forma általában gyorsabb kódot és áttekinthetőbb listát eredményez. A C programokban természetesen mindkettő forma használható, de igyekezzünk a másodikat előnyben részesíteni. A mellékelt táblázatban összefoglaljuk ezeket a rövidített értékadásokat.
Hagyományos forma
Tömör forma
a = a + b
a += b
a = a - b
a -= b
a = a * b
a *= b
a = a / b
a /= b
a = a % b
a %= b
a = a << b
a <<= b
a = a >> b
a >>= b
a = a & b
a &= b
a = a | b
a |= b
a = a ^ b
a ^= b
Feltételes operátor
szerkesztés
A C-ben ez az egyetlen operátor, melynek három operandusa van. Általános alakja a következő:
kifejezés1 ? kifejezés2 : kifejezés3;
Itt először a kifejezés1 értékelődik ki, ha ennek értéke nem 0, azaz IGAZ, akkor a kifejezés2 adja a feltételes kifejezés értékét, különben pedig a kifejezés3. A feltételes kifejezés típusa mindig a kifejezés2 és kifejezés3 típus közül a nagyobb pontosságú típusával egyezik meg.
A következő példában c értéke a és b közül a kisebbel lesz egyenlő.
Mutató (Pointer) operátorok
szerkesztés
A mutatókról az eddigiek során még nem volt szó, de a teljesség kedvéért megemlítünk két egyoperandusú műveletet, mely a mutatókhoz kötődnek. A
a címképző operátor.
int
ptr
ptr
Ebben a példában a
ptr
egy egész típusú változóra mutat, értékadásnál pedig az a változó memóriabeli címét kapja meg. Ha erre a címre új értéket akarunk írni, akkor a
indirekció (vagy dereferencia) operátort kell használnunk.
ptr
egyenértékű az
értékadással.
Típuskonverziók
szerkesztés
A C-ben kétfajta típuskonverzió létezik, az implicit (automatikus) és az explicit. Az első a C nyelvbe rögzített szabályok szerint történik a programozó beavatkozása nélkül, a második pedig a típuskonverziós operátor segítségével. Ennek általános alakja:
(típusnév) kifejezés
Az implicit konverzióval kapcsolatban elmondhatjuk, hogy általában a szűkebb operandus információveszteség nélkül konvertálódik a szélesebb operandus típusára.
int
float
Ebben az esetben az i float-ra konvertálódik.
Itt viszont vigyáznunk kell, mert adatvesztés lép fel, az összeg törtrésze elveszik.
Explicit konverziót kell végrehajtanunk a következő példában, ha
-be nem csak az egész osztás hányadosát szeretnénk betenni.
int
12
float
float
float
A következő táblázatban összefoglaltuk az említett műveleteket precedenciájuk szerint rendezve. Valamint a kiértékelés sorrendjét is megadtuk. A kiértékelés sorrendje akkor kerül előtérbe, ha egy kifejezésben egyenlő precedenciájú operátorok szerepelnek zárójelezés nélkül.
Operátor
Kiértékelés sorrendje
! ~ - ++ -- & * (típus)
Jobbról balra
/ &
Balról jobbra
+ -
Balról jobbra
<< >>
Balról jobbra
< <= > >=
Balról jobbra
== !=
Balról jobbra
Balról jobbra
Balról jobbra
Balról jobbra
&&
Balról jobbra
||
Balról jobbra
?:
Balról jobbra
= += -= *= /= %=
<<= >>= &= |= ^=
Jobbról balra
Adatok beolvasása a billentyűzetről
szerkesztés
A formázott adatbeolvasást a
scanf
()
függvény segítségével tehetjük meg. A függvény általános formája a következő:
scanf(formátum, argumentumlista);
scanf
()
karaktereket olvas a billentyűzetről, majd a formátum alapján értelmezi azokat, ha a beolvasott karakterek megfelelők, akkor konvertálja őket. Ha az input valamilyen ok miatt nem felel meg a formátum előírásainak, akkor a
scanf
()
befejezi az olvasást, még akkor is, ha az argumentumlista szerint további karaktereket is be kellene olvasnia. A
scanf
()
függvénynek visszatérési értéke is van. A sikeresen beolvasott adatok számát adja vissza. Nézzünk néhány példát a
scanf
()
használatára.
int
char
printf
"Kérek egy egész számot és egy betűt"
);
scanf
"%d%c"
);
A példából látszik, hogy az egyszerű adatokat cím szerint kell beolvasni. Az argumentumlistában az
&a
és az
&c
a változók memóriabeli címére utal. A formátumban ugyanazokat a karaktereket használhatjuk, mint a
printf
()
esetében korábban tettük. Ez a sor egy számot és egy karaktert olvas be egymás után, nem tesz közéjük semmilyen elválasztó jelet. Nyilván, ha egy
scanf
()
-fel több értéket is akarunk beolvasni, akkor valamilyen határolóra szükség van.
int
char
printf
"Kérek egy egész számot és egy betűt vesszővel elválasztva"
);
scanf
"%d,%c"
);
Figyeljük meg a változtatást. A formátumban egy vesszőt tettünk a második % jel elé. Ilyenkor a
scanf
()
beolvassa a vesszőt is, de azt nem tárolja. Ilyen módon bármilyen határoló karaktereket előírhatunk beolvasáskor.
scanf
()
segítségével sztringeket (karakterláncokat) is olvashatunk be. Ebben az esetben nem használjuk az & operátort.
char
sz
30
];
scanf
"%s"
sz
);
scanf
()
egy hasznos lehetősége, hogy az adatok szűrését is lehetővé teszi. Az előbbi deklaráció szerinti
sz
változó értéke csak számjegy, vagy hexadecimális jegy lehet, akkor azt a következő szűréssel valósíthatjuk meg:
scanf
"%[0-9a-fA-F]"
sz
);
A komplementer halmaz megadására is van módunk:
scanf
"%[^0-9]"
sz
);
Ezzel a szűréssel csak betűk kerülhetnek a sztringbe.
Iterációk
szerkesztés
Mint minden magas szintű programozási nyelvben, a C-ben is vannak olyan utasítások, melyek egy feltételtől függően többször is végrehajtják ugyanazt az utasítást, vagy utasítás blokkot. A következőkben ahol utasítást írunk helyettesíthető utasításblokkal is. Utasításblokk:
utasítás1;
utasítás2;
utasításn;
while ciklus
szerkesztés
while
ciklus általános alakja:
while (kifejezés)
utasítás;
A ciklusmag utasítása addig hajtódik végre, amíg a kifejezés értéke nem nulla. (Ez a logikai igaznak felel meg, tehát, ha a kifejezés egy logikai kifejezés, akkor itt a ciklusba lépés feltételét adjuk meg.)
Nézzünk egy példát a
while
ciklusra. Adjuk össze 1-től n-ig a természetes számokat!
#include
int
main
()
long
osszeg
int
2000
printf
"Az első %d egész szám összege: "
);
while
<=
osszeg
+=
++
printf
"%ld"
osszeg
);
return
printf
()
-ben látható
ld
-ben az l hosszú (
long
) egészre utal.
while
ciklust gyakran szoktuk használni arra, hogy egy bizonyos billentyű leütésére várakozzunk.
while
((
ch
getch
())
!=
27
);
Az ESC billentyű leütésére vár.
for ciklus
szerkesztés
A for ciklust a leggyakrabban akkor használjuk, ha előre tudjuk, hogy egy utasítást hányszor akarunk végrehajtani. Az utasítás általános alakja egyszerű formában a következő lehet:
for (kifejezés1; kifejezés2; kifejezés3)
utasítás;
A kifejezés1-ben állítjuk be a ciklusváltozó kezdő értékét, a kifejezés2-ben a ciklusba való lépés feltételét, ez a leggyakrabban egy logikai kifejezés, a ciklusmag után a kifejezés3-ban pedig léptetjük a ciklusváltozót.
Példaként írjuk át az előző programot
for
ciklust használva.
#include
int
main
()
long
osszeg
int
2000
for
osszeg
<=
++
osszeg
+=
printf
"Az első %d szám összege: %ld"
osszeg
);
return
Figyeljük meg a for ciklus fejében a
operátort. Ezt a korábbiakban nem említettük, szerepe az, hogy egy utasításban több kifejezést is elhelyezhetünk. Itt az osszeg=0 értékadás még a kifejezés1 része. Mivel a ciklus magja összesen egy utasítást tartalmaz, ezért a fenti for ciklus lényegesen rövidebben is leírható:
for
osszeg
<=
osszeg
+=
++
);
Itt feltétlenül kell egy
az utasítás végére, ezzel jelezzük, hogy a for ciklus csak egy üres utasítást tartalmaz. Vigyázzunk azonban, ha egy egyéb utasításokat tartalmazó for ciklusban erre a helyre
-t teszünk meglepődve tapasztalhatjuk, hogy a ciklusmag többi utasítása nem fog végrehajtódni.
do-while ciklus
szerkesztés
Ezt a ciklust igen ritkán használjuk. Minden programozási feladat megoldható az előző két ciklus alkalmazásával, van azonban néhány olyan feladat (pl. a bináris keresés), mely rövidebb kódot eredményez, ha a do-while ciklust használjuk. A ciklus általános alakja:
do
utasítás;
while (kifejezés);
A ciklusba itt is addig lépünk, amíg a kifejezés értéke nem 0, logikai kifejezés esetén amíg a kifejezés igaz. Alapvetően abban különbözik az előző két ciklustól, hogy itt a ciklusmag utasítása legalább egyszer végrehajtódik.
Nézzük a következő példát a do-while ciklusra. Bekérünk egy egész számot és ha többjegyű, akkor fordított sorrendben írjuk ki.
#include
int
main
()
int
szam
jegy
printf
"Kérek egy egész számot:"
);
scanf
"%d"
szam
);
printf
\n
A fordítottja: "
);
do
jegy
szam
10
printf
"%d"
jegy
);
szam
/=
10
while
szam
!=
);
return
11., 12., 13. feladat
Szelekciók
szerkesztés
A C nyelvben három elágazás típust használhatunk. Az egyágú (if szerkezet), a kétágú (if-else szerkezet) és a többágú (switch szerkezet)
2.8.1 if utasítás
Az utasítás általános alakja:
if
kifejezés
utasítás
Itt az utasítás csak akkor hajtódik végre, ha a kifejezés nem nulla. (IGAZ)
Példaként kérjünk be egy lebegőpontos számot, de csak az ]1,10[ intervallumba eső értéket fogadjuk el jó értéknek.
#include
int
main
()
float
printf
"Kérek egy lebegőpontos számot
\n
);
printf
"1 és 10 között: "
);
scanf
"%f"
);
if
((
1.0
&&
10.0
))
printf
"Jó érték!!"
);
return
2.8.2 if-else szerkezet
Az utasítás általános alakja:
if
kifejezés
utasítás1
else
utasítás2
Ha a kifejezés értéke nem nulla (IGAZ), akkor az utasítás1 hajtódik végre, ha pedig nulla (HAMIS) az utasítás2.
Az előző példánál maradva, ha az érték rossz, akkor írassuk ezt ki az else ágon!
else
printf
"Az érték rossz"
);
Ezt a két sort az előző programban az üres sor helyére kell beszúrni. Vigyázzunk azonban, ebben az esetben a Jó érték sorának végén nem állhat ; ugyanis ekkor egy else kulcsszóval találkozna a fordító, amivel viszont nem kezdődhet parancs.
2.8.3 switch utasítás
Az utasítás több irányú elágazást tesz lehetővé, de csak abban az esetben, ha egy egész kifejezés értékét több konstanssal kell összehasonlítani. Általános alakja:
switch
Kifejezés
case
konstans1
utasítás1
case
konstans2
utasítás2
default
utasításn
switch
utasítással nagyon átgondoltan kell bánni. Általános esetben ha az egyik
case
-nél találunk egy belépési pontot, akkor az utána következő
case
cimkék után álló utasítások is végre fognak hajtódni. Hacsak nem pontosan ez a szándékunk, akkor minden utasítást
break
-kel kell zárni, melynek hatására a vezérlés a
switch
utáni első utasításra kerül. Általában a default eseteket is break-kel szoktuk zárni, mert ez nem feltétlenül az egyes esetek végén áll, bárhol elhelyezhető a szerkezetben. Ha ugyanazt az utasítást akarjuk végrehajtani több konstans érték esetén is, akkor a konstansokat egymástól
-tal elválasztva soroljuk fel.
Nézzünk egy rövid példaprogramot a
switch
illusztrálására:
#include
int
main
()
int
n1
n2
printf
"Írjon be két számot: "
);
scanf
"%d %d"
n1
n2
);
printf
\n
Válasszon
\n
);
printf
"1=Összeadás
\n
);
printf
"2=Kivonás
\n
);
scanf
"%d"
);
switch
case
n1
n2
break
case
n1
n2
break
default
printf
"Nem jó választás
\n
);
if
==
printf
"%d + %d = %d
\n
n1
n2
);
else
if
==
printf
"%d - %d = %d
\n
n1
n2
);
return
Figyeljük meg, hogy minden választás után használtuk a
break
-et. A default után nem használtuk, de ha a
switch
elején van, akkor ott is érdemes odafigyelni rá.
14., 15., 16., 17., 18., 20. feladat
Tömbök
szerkesztés
Az eddigiek során olyan változókat használtunk csak, melyek egy érték tárolására voltak alkalmasak. Gyakran van azonban szükségünk arra, hogy ugyanazzal a változónévvel több értékre is tudjunk hivatkozni. Ebben az esetben használjuk a tömböket (arrays). (Egy osztály minden tanulójához hozzárendeljük a magasságát, ilyenkor nyilván érdemes változónévként az osztály azonosítóját használni, a tanulókra pedig a napló sorszámával hivatkozni.)
2.9.1 Egydimenziós tömbök
A deklaráció szintaxisa:
típus
tömbnév
méret
];
Konkrét példák:
int
10
];
char
betuk
];
Hivatkozások a Pascalban is megszokott módon történhetnek:
betuk
"C"
23
A C-ben a tömb elemeinek indexelése minden esetben 0-tól indul az első elemmel, azaz a fenti példákban vigyázni kell, mert nem hivatkozhatunk a[10]-re, mert az már a tömb 11. eleme lenne.
A tömböknek már a létrehozáskor is adhatunk kezdőértéket. Ennek szintaxisa a következő lehet:
int
};
int
[]
11
22
33
};
Az első esetben megmondtuk a tömb méretét és ennyi darab elemet is soroltunk fel, ha több elemet adtunk volna meg, akkor a fordító hibajelzéssel leáll, ha kevesebbet, akkor pedig a változó tárolási osztályától függően vagy 0, vagy határozatlan értékű lesz. A második esetben nem adtuk meg az elemek számát a szögletes zárójelben, csak felsorolással, ilyenkor a tömb pontosan 7 elemű lesz.
A tömb méretét akár futásidőben is meg tudjuk mondani. Ha egy program tesztelése során változtatjuk egy tömb méretét (új elemeket veszünk fel a fölsorolásba), akkor érdemes a definíciós részben a következő sor beszúrni:
#define MERET(b) (sizeof(b)/sizeof(b[0]))
Példaként nézzük meg hogyan lehet egy 10 hosszúságú tömb elemeit beolvasni, és összegezni:
#include
#define SZAM 10
int
main
()
int
10
];
int
for
SZAM
++
printf
"a[%d]="
);
scanf
"%d"
]);
+=
];
printf
\n
Az összeg: %d"
);
return
2.9.2 Sztringek
A C-ben nincs külön sztring típus, ezért az egy dimenziós tömböket gyakran használjuk sztringek definiálására. Tulajdonképpen a sztring egy egydimenziós karaktertömb lesz. A sztring végét egy "\0" karakter fogja jelezni, ezért ha egy 10 hosszúságú sztringet szeretnénk kezelni, akkor azt feltétlenül 11 hosszúságúra kell létrehoznunk.
char
sztring
11
];
char
sz
[]
"C"
"-"
"n"
"y"
"e"
"l"
"v"
\0
);
Értékadás helyett célszerűbb és biztonságosabb a következő megadási mód:
char
sz
[]
"C-nyelv"
Ebben az esetben ugyanis a sztring végét jelző \0 karaktert a fordító teszi ki a sztring végére.
A következő táblázatban néhány sztingkezelő függvényt sorolunk föl.
Név Leírás Példa
strcat(sz1,sz2) Az sz1 sztringhez fűzi az sz2 sztringet sz1="hello"; sz2="world"
strcat(sz1,sz2);
printf(" %s\n",sz1)
Eredménye: helloworld
strcpy(sz1,sz2) Az sz1 stringbe másolja az sz2 sztringet sz2="world"
strcat(sz1,sz2);
printf(" %s\n",sz1)
Eredménye: world
strcmp(sz1,sz2) Összehasonlítja a két sztringet, ha egyenlők, akkor 0, ha nem egyenlők, akkor nem 0 értékkel tér vissza. sz1="Hello"; sz2="HeLlO"
n=strcmp(sz1,sz2);
printf("%d\n",n)
Eredménye: 32
strcmpi(sz1,sz2) Ugyanaz, mint az előző, csak a kis és a nagy betűk között nem tesz különbséget. Az előző adatokkal
Eredménye: 0
strchr(sz1,c) Megnézi, hogy az sz1 stringben először hol fordul elő a c karakter char c=’e’, sz1[]="Hello", *ch;
ch=strchr(sz1,c);
printf("%d\n", ch-sz1);
Eredménye: 1
strrchr(sz1,c) Ugyanaz, mint az előző, csak jobbról indul
strlwr(sz), strupr(sz) A sztringet kis, illetve nagy betűs formátumúvá alakítja sz="hello"
printf("%s\n",strupr(sz));
Eredménye: HELLO
strncpy(sz1,sz2,n) Az sz1 sztringbe másol sz2-ből n db karaktert; sz2="Hello";n=2;
strncpy(sz1,sz2,n);sz[2]="\0" ;
printf("%s\n",sz1);
Eredménye: He
strncat(sz1,sz2,n) Az sz1 sztringhez fűz n db-ot az sz2-ből sz1="Hello"; sz2="World"; n=2;
strncat(sz1,sz2,n);
printf("%s\n",sz1);
Eredménye: HelloWo
strrev(sz) Megfordítja a sztringet sz1="Hello";
printf("%s\n",strrev(sz))
Eredménye:olleH
strlen(sz) Egy sztring hosszát adja meg n=strlen(sz);
Ha ezeket a sztringkezelő függvényeket használni akarjuk,akkor mindenképpen szükség van a
#include
beszúrásra.
Nézzünk egy példaprogramot arra, hogyan lehet egy sztringet karakterenként végigolvasni anélkül, hogy tudnánk a hosszát:
#include
int
main
()
char
sz
[]
"C programozási nyelv"
int
while
sz
])
printf
\n
%c"
sz
]);
++
return
21., 22., 23., 24., 25., 26. feladat
2.9.3 Kettő és több dimenziós tömbök
A C nyelvben is lehetőségünk van kettő vagy több dimenziós tömbök használatára. Deklarálni a következőképpen kell:
típus
név
méret1
méret2
Konkrét példaként egy lebegőpontos értékeket tartalmazó két dimenziós tömbre:
float
][
];
Az első index ebben az esetben a sor index, a második pedig az oszlop index. A hivatkozás ennek megfelelően:
][
4.73
Lehetőség van itt is a kezdőérték megadására:
int
matrix
][
},
},
}};
Nagyon fontos, hogy az indexek itt is minden esetben 0-tól indulnak!
Egy két dimenziós tömb táblázatos megjelenítése:
#include
#include
#include
#define SOR 4
#define OSZLOP 3
int
main
()
int
SOR
][
OSZLOP
];
int
clrscr
();
randomize
();
for
SOR
++
for
OSZLOP
++
][
rand
()
100
for
SOR
++
for
OSZLOP
++
printf
"%d
\t
][
]);
printf
\n
);
return
27. feladat
Felhasználó által definiált típusok
szerkesztés
Az előző részben említett összetett adattípust tömböknek neveztük, a tömbök minden eleme azonos típusú kell hogy legyen. Gyakran szükségünk van azonban olyan egymással összetartozó elemek tárolására is, melyek nem azonos típusúak, mégis egy egyedre jellemzők. Ezeket leggyakrabban adatbázisokban találjuk meg. Gondoljunk itt egy személy kereszt és vezetéknevére, születési idejére, alapbérére. Ezek különböző típusú adatok mégis ugyanarra a személyre vonatkoznak.
2.10.1 Struktúrák
Egy ilyen adattípust valósítanak meg a struktúrák. Ennek deklarációja általános formában:
struct
név
típus1
tag1
típus2
tag2
};
A bevezetőben említett konkrét példában leírt struktúra:
struct
szemely
char
vnev
20
];
char
knev
15
];
int
szev
float
ber
};
Ha ilyen típusú változót akarunk létrehozni, akkor annak módja:
struct
szemely
sz
Erre a változóra a nevével és a struktúrán belüli tag nevének megadásával hivatkozhatunk a
(pont) operátor segítségével:
sz
vnev
"Kovács"
A struktúrát a
typedef
-fel együtt is használhatjuk. Ilyenkor a
struct
utáni azonosító el is maradhat, és csak utána kerül a típust azonosító név:
typedef
struct
char
vnev
20
];
char
knev
15
];
int
szev
float
ber
szemely
Ebben az esetben természetesen a változó típusának megadásakor is hiányzik a
struct
szemely
sz
2.10.2 Struktúrát tartalmazó tömbök
Ha egy struktúra típusú változót létrehoztunk, akkor annak segítségével csak egyetlen egyed jellemzőit tudjuk tárolni. Mit tehetünk, ha több egyedről is ugyanazokat a jellemzőket szeretnénk raktározni? Kézenfekvő megoldás olyan tömbök alkalmazása, melynek minden egyes eleme az adott struktúra típusú.
Az előző példánál maradva egy vállalatnak maximálisan 20 dolgozójáról a fenti adatokat szeretnénk tárolni, akkor a
szemely
sz
20
];
adatszerkezettel dolgozhatunk. Az adatszerkezet egyes elemeire való hivatkozáskor a struktúra és a tömb hivatkozásokat vegyesen alkalmazzuk.
sz
].
szev
1961
28. feladat
Függvények
szerkesztés
Függvényeket a következő esetekben szokás írni:
Ha ugyanazt a tevékenységsorozatot többször is el kell végeznünk ugyanolyan típusú, de más-más értéket fölvevő változókkal.
Ha a programunkat strukturáltan, jól olvashatóan szeretnénk megírni. Ez nagyon fontos a későbbi módosíthatóság miatt.
Természetesen az első kettő eset együtt is fennállhat.
A függvény definíció általános alakja:
visszatérési_érték_típus
fvnév
típus1
vált1
típus2
vált2
függvény
teste
return
visszatérési_érték
Konkrét példa egy egyszerű függvényre:
int
osszeg
int
int
int
return
Ha egy függvénynek nincs visszatérési értéke, akkor a
void
kulcsszót használjuk visszatérési típusnak, és a
return
opcionális:
void
fnev
típus1
vált1
2.11.1 Paraméterátadás
A paraméterátadás a Pascal-hoz hasonlóan itt is történhet cím szerint és érték szerint. Az elve szintén ugyanaz. Az érték szerinti paraméterátadásra az iménti függvény lehet egy példa. Cím szerinti paraméterátadásnál azonban már sokkal jobban oda kell figyelni, mint a Pascalban.
Példaként írjunk egy függvényt, mely a paraméterben megkapott változókat fölcseréli.
#include
#include
void
csere
int
int
int
int
main
()
int
printf
"%d, %d"
);
csere
);
printf
\n
%d, %d"
);
return
Nézzük meg figyelmesen a listát! Már rögtön a függvény fejében észrevehetünk egy változást, a
-ot használjuk, ezzel a változó memóriabeli helyén található értékre utalunk. A másik változás a függvényhíváskor figyelhető meg, az
C operátort használjuk, ezzel jelezzük azt, hogy nem a változó értékét, hanem a változó memóriacímét adjuk át. Nyilván a függvényben emiatt kell használnunk a
operátort. Ezek az operátorok a mutatókhoz kötődnek, a későbbiekben még lesz róluk szó.
Vegyük észre azt is, hogy a függvénynek nincs visszatérési értéke, annak ellenére, hogy az átadott változók értéke megváltozott.
29., 30., 31. feladat
2.11.2 Lokális és globális változók
A lokális változókat csak azok a blokkok látják, ahol deklarálva lettek, ez alól a
main
()
sem kivétel. Ezek a változók a veremben tárolódnak, tehát minden függvényhíváskor törlődik az értékük.
A globális változókat minden függvény látja, értéküket módosíthatja. Ezeket a változókat minden függvény törzsén kívül kell deklarálni. Célszerű a preprocesszor-utasítások után és a függvénydeklarációk előtt megtenni ezt. Ajánlott a globális változókat egy helyen deklarálni, lehetőség van ugyan arra, hogy két függvény között is deklaráljunk változót, ebben az esetben viszont az előbb lévő függvény nem látja az alatta lévő változót, annak ellenére sem, hogy az függvényeken kívül lett létrehozva.
2.11.3 Automatikus és statikus változók
Bevezetésként tanulmányozzuk a következő programot:
#include
#include
void
demo
()
static
int
sv
auto
int
av
printf
"sv=%d, av=%d
\n
sv
av
);
++
sv
++
av
int
main
()
char
int
clrscr
();
while
demo
();
++
while
((
getch
())
!=
13
);
return
A program kimenete:
sv=0, av=0
sv=1, av=0
sv=2, av=0
A statikus változókat csak egyszer hozza létre a fordító és értéket is csak egyszer kapnak, a legelső függvényhíváskor. Statikus változót lokálisan és globálisan is létrehozhatunk, értékét a program futása során végig megtartja, a különbség a kettő között csak a láthatóságban van. A lokálisan létrehozott statikus változó csak a blokkján belül látható. A fenti program egy lokális statikus változót mutatott be.
Az automatikus változók minden függvényhíváskor újra deklarálódnak, és mindig fölveszik a megadott kezdőértéket. Ha nem írjuk ki ezeket a módosító jelzőket, akkor a változó automatikus lesz. Az automatikus változók dinamikusan tárolódnak a veremben.
2.11.4 Register módosító jelző
A register kulcsszó azt jelenti a fordító számára, hogy az adott változóhoz gyorsan szeretnénk hozzáférni. Arra utasítjuk tehát, hogyha módjában áll, akkor ne a memóriában, hanem regiszterekben tárolja. Nyilván akkor folyamodunk ilyen technikához, ha úgy gondoljuk, hogy az adott változót gyakran módosítjuk. Regiszterbe
char
int
és mutató típust tehetünk az esetek többségében.
Arra semmi garancia nincs, hogy a fordító tudja teljesíteni kérésünket, de ha tudja, akkor az eredmény gyorsabb futás lesz.
void
csere
int
int
int
2.11.5 Tömb átadása függvénynek
A következő példa bemutatja, hogy egy tömböt hogyan adhatunk át paraméterként egy függvénynek. A függvény az átadott tömb legnagyobb elemével tér vissza.
#include
#include
#include
int
maximum
int
[],
int
int
int
max
];
for
++
if
max
])
max
];
return
max
int
main
()
char
int
[]
12
11
13
21
10
34
};
clrscr
();
printf
\n
a b[] maximuma: %d"
maximum
sizeof
sizeof
])));
while
((
getch
())
!=
13
);
return
A programot vizsgálva adódhat az az ötletünk, hogy a tömb méretét fölösleges volt átadni a függvénynek, azonban ez sajnos nem igaz. A függvény fejében lévő int
a[]
ugyanis csak annyit közöl a fordítóval, hogy egy egészeket tartalmazó tömb fog jönni argumentumként.
Mutatók (Pointerek)
szerkesztés
A C programozási nyelvben van egy különös adattípus: a mutató. Ez egy változónak a memóriabeli címét veheti föl értékül. Definiálás során a mutató típusú változó neve előtt a
jelet kell használnunk. (Ezt nevezik indirekt operátornak is.) Egy mutató értékét egy változóból a
címképző operátorral kaphatjuk. A mutató által mutatott változó értékét a
operátorral kaphatjuk meg. Nézzünk erre egy konkrét példát:
int
/* a p egy mutató, ami egész típusú változóra mutat */
/* p az a címét veszi föl értékként */
/* b megkapja a p által mutatott címen lévő értéket, ebben az esetben 3 lesz */
A következő programban mi fog megjelenni a két kiírás után?
#include
#include
int
main
()
char
int
10
15
clrscr
();
=&
+=
printf
"%d
\n
);
+=
printf
"%d
\n
);
while
((
getch
())
!=
13
);
return
A mutatóknak nem ez a bűvészkedés adja a jelentőségét. Segítségükkel dinamikusan kezelhetjük a memóriát, mindig csak annyit használva, amennyire az adatoknak éppen szüksége van.
32. feladat
2.12.1 Tömbök és mutatók
A C programozási nyelvben igen szoros kapcsolat van a a tömbök és az egyszeres indirektségű mutatók között. Ez a tömbök tárolási módjából ered. A tömbök a memóriában sorfolytonosan helyezkednek el. Ha egy mutatót a tömb első elemére irányítunk, akkor a mutató aritmetika szabályai szerint ehhez 1-et hozzáadva a tömb második elemét fogjuk megkapni.
int
10
];
];
Ekkor a
hivatkozás a tömb első elemét fogja jelenteni. Teljesen egyenértékű a következő két hivatkozás:
Mivel a kapcsolat ilyen szoros a tömb és a tömb első elemére mutató mutató között, ezért a tömbös és a mutatós hivatkozások felcserélhetők. A fenti deklarációk szerint a tömb i-edik elemére való hivatkozások:
],
],
),
Az első kettő tömbös, a második kettő pedig mutató típusú hivatkozás. Jól jegyezzük meg tehát, hogy az a tömbnév és a
mutató is az elemek sorozatának első elemét jelenti.
Pascal programokban, ha szükségünk volt egy tömbre, akkor azt már változó deklarációban létre kellet hoznunk és a méretét is be kellett állítanunk. Ha a tömb elemszáma elérheti a 100-at is, de az esetek 99%-ában nekünk csak 10 elemre van szükségünk, nem tehettünk mást, mint hogy 100 hosszúságúra hoztuk létre a tömböt, ezzel jelentős memória területet lefoglaltunk. A C nyelvű programokban nagyon egyszerűen létrehozhatunk dinamikus helyfoglalású tömböket. Ezeknél futási időben dől el, hogy milyen hosszúságúak lesznek, ha a további feldolgozáshoz nincs szükség rájuk, akkor az általuk lefoglalt memória felszabadítható.
Nézzünk erre egy példaprogramot. A program megkérdezi, hogy hány elemű tömbbel kívánunk dolgozni, majd helyet foglal a memóriában az elemeknek, véletlen számokkal feltölti a tömböt, kiírja az elemeket és összegüket, majd a végén felszabadítja a lefoglalt memória területet.
#include
#include
#include
int
main
()
char
unsigned
int
long
int
osszeg
clrscr
();
randomize
();
printf
"Hány adattal akarsz dolgozni: "
);
scanf
"%u"
);
/* Helyfoglalás a memóriában, p az első helyre mutat */
unsigned
int
calloc
sizeof
unsigned
int
));
if
printf
"Nincs elég hely a memóriában"
);
return
-1
for
++
random
5000
);
/* Tömbös hivatkozás */
for
++
printf
"%u,"
));
/* Mutatós */
osszeg
+=
);
/* hivatkozás */
printf
\n
%ld"
osszeg
);
free
);
/* Felszabadítja lefoglalt memóriát */
while
((
getch
())
!=
13
);
return
Magyarázatok a fenti programhoz: Ha a memóriakezelő függvényeket akarjuk használni, akkor szükségünk van az stdlib.h beemelésére. A helyfoglalást a memóriában a
calloc
()
, vagy a
malloc
()
függvények hívásával végezhetjük el. A
calloc
()
függvénynél meg kell mondanunk, hogy hány elem számára szeretnénk helyet foglalni, és hogy egy elemnek mekkora a mérete, ebben a sorrendben. A függvény a lefoglalt memóriaterületet rögtön feltölti nullával. A
malloc
()
függvénnyel pedig azt kell közölni, hogy mekkora memóriaterületet szeretnénk lefoglalni (byte-ban).
Ha már nincs szükségünk a lefoglalt területre, akkor ezt a
free
()
függvénnyel fölszabadíthatjuk.
Figyeljük meg a p-nek történő értékadást! A
calloc
()
függvény visszatérési értéke egy típus nélküli mutató, ezt egy típuskonverzióval át kellet alakítani a p típusának megfelelő alakra. Minden esetben meg kell vizsgálni, hogy sikeres volt-e a helyfoglalás. Ha nincs elég memória, akkor a
calloc
()
függvény
NULL
értékkel tér vissza, ebben az esetben a
return
hatására a program kilép a
main
()
függvényből és befejezi futását.
A programban a mutatós és tömbös hivatkozás vegyesen lett használva, mutatva ezzel a kettő teljes egyenértékűségét.
2.12.2 Kétdimenziós tömbök és mutatók
Természetesen két dimenziós tömböket is lehet dinamikusan kezelni. Erre három különböző módszert mutatunk be. Mindhárom program csupán annyit csinál, hogy egy mátrixot feltölt véletlen számokkal, majd táblázatos formában megjeleníti. A programok után rövid magyarázatok is lesznek.
2.12.2.1 Dinamikus többdimenziós tömb
#include
#include
#include
int
main
()
char
int
int
clrscr
();
randomize
();
printf
"Sorok száma: "
);
scanf
"%d"
);
printf
"Oszlopok száma: "
);
scanf
"%d"
);
int
calloc
sizeof
int
));
if
printf
"Nincs elég memória!"
);
return
-1
for
++
for
++
random
10
);
/* Tömbös hivatkozás */
printf
"%3d "
));
/* Mutatós hivatkozás */
printf
\n
);
free
);
while
((
getch
())
!=
13
);
return
A memóriafoglalás ebben a programban valóban futási időben történik. Ha közelebbről is szemügyre vesszük a tömböt látható, hogy itt a többdimenziós tömböt valójában egy tömbben tároljuk. Az aktuális pozíciót pedig a
hely
aktuális_sor
oszlopszám
aktuális_oszlop
formulával határoztuk meg. Figyeljünk föl itt is a két fajta hivatkozásra!
2.12.2.2 Konstansban megadott tömbméret, dinamikus helyfoglalással
#include
#include
#include
#define N 6
#define M 5
typedef
int
matrix
][
];
int
main
()
char
matrix
int
clrscr
();
matrix
calloc
sizeof
matrix
));
if
printf
"Nincs elég memória!"
);
return
-1
for
++
for
++
)[
][
random
10
);
printf
"%3d "
)[
][
]);
printf
\n
);
free
);
while
((
getch
())
!=
13
);
return
Ebben az esetben futási időben nincs már lehetőségünk a tömb méreteinek változtatására, de a helyfoglalás itt is csak futási időben történik meg. A hivatkozás majdnem teljesen olyan, mint a mátrixok esetében szokásos. Itt a
calloc
()
függvényben érdekes módon csak 1 elem számára kell helyet foglalni, ami viszont akkora, mint a teljes mátrix.
2.12.2.3 Mutatótömb
#include
#include
#include
#define N 5
int
main
()
char
int
],
oszlop
];
int
clrscr
();
for
++
printf
"%d. sor oszlopszáma: "
);
scanf
"%d"
oszlop
]);
for
++
int
calloc
oszlop
],
sizeof
int
));
if
])
printf
"Nincs elég memória!"
);
return
-1
for
++
for
oszlop
];
++
random
10
);
printf
"%3d "
][
]);
printf
\n
);
for
++
free
]);
while
((
getch
())
!=
13
);
return
Itt a mátrixot egy mutatótömbbel valósítottuk meg. A különlegesség az, hogy a sorok eltérő hosszúságúak is lehetnek. A mutatóknak egyesével adtunk értéket, azaz a helyfoglalás soronként történt. A felszabadítást szintén ciklus segítségével kell végezni.
33., 34. feladat
2.12.3 Struktúrák és mutatók
A korábbiakban már volt szó a struktúrákról. Mutatókkal hivatkozhatunk a struktúrák egyes elemeire is, mint azt a következő példában láthatjuk.
struct
datum
int
ev
ho
nap
};
struct
datum
map
Az így létrehozott mutatónak értéket adhatunk, illetve értékét lekérdezhetjük.
map
).
ev
2001
map
).
ho
map
).
nap
14
Mivel a C-ben elég gyakori a struktúrák és a mutatók használata, a fenti hivatkozásoknak van egy másik alakja is.
map
->
ev
2001
map
->
ho
map
->
14
A két hivatkozás egymással teljesen egyenértékű, de a C programokban és a szakirodalomban a második lényegesen elterjedtebb.
2.12.4 Dinamikus lista
A dinamikus lista (láncolt lista) olyan adatszerkezet, melynek minden egyes eleme két jól elkülöníthető részből áll, az adatrészből és egy mutatóból, mely a lista következő elemére mutat. A lista első elemére a listafejjel hivatkozhatunk. A lista utolsó elemének mutató mezője pedig
NULL
értékű.
A fenti ábrán a szürke az adatrész a fekete pedig a mutató rész.
A lista szerkezetnek sok előnye van egy tömbhöz képest. Egy listában tényleg mindig csak annyi elem van, amennyire éppen szükségünk van. A szerkezet módosítása is nagyon egyszerű. Ha egy új adatot akarunk felvenni, akkor:
1. megkeressük az új elem helyét
2. az aktuális adat mutatóját átmásoljuk az új elem mutatójába
3. az aktuális elem mutatóját az új elemre irányítjuk
Ha egy listából egy elemet törölni akarunk, akkor a következőképpen járhatunk el:
1. megkeressük a törlendő elemet
2. a mutatóját bemásoljuk az előtte lévő elem mutatójába
Nézzük ezt először egy egyszerű programon keresztül. Először létrehozunk egy struktúrát, a struktúra mutató mezője egy ugyanilyen típusú struktúrára hivatkozik, ezt nevezzük önhivatkozó struktúrának.
#include
struct
lista
int
value
struct
lista
next
};
int
main
()
struct
lista
n1
n2
n3
n4
struct
lista
lista_pointer
n1
n1
value
100
n1
next
n2
n2
value
200
n2
next
n3
n3
value
300
n3
next
n4
n4
value
400
n4
next
while
lista_pointer
!=
printf
"%d
\n
lista_pointer
->
value
);
lista_pointer
lista_pointer
->
next
return
Ez még így egyáltalán nem dinamikus, csak a lista használatát figyelhetjük meg rajta. Vegyük észre, hogy az a sok értékadás a listázás előtt, ciklikus folyamat, nyilván nem érdemes ilyen sokszor leírni.
n1
next
n2
next
n2_3
next
n2
next
n2
next
n2_3
Mi történik a listával a fenti értékadások hatására?
A következő program már egy dinamikus lista megvalósítására mutat példát.
#include
#include
#include
struct
data
int
value
struct
data
nxt
};
struct
data
head
NULL
prev
akt
next
void
list
()
akt
head
while
akt
!=
NULL
printf
"%5d"
akt
->
value
);
akt
akt
->
nxt
int
main
()
char
int
sv
clrscr
();
randomize
();
printf
"Következő szám "
);
scanf
"%d"
sv
);
while
sv
akt
struct
data
malloc
sizeof
struct
data
));
if
akt
printf
"Nincs elég memória!"
);
return
-1
akt
->
value
sv
akt
->
nxt
NULL
if
==
head
prev
akt
else
prev
->
nxt
akt
prev
akt
printf
"Következő szám "
);
scanf
"%d"
sv
);
++
printf
\n
);
list
();
while
((
getch
())
!=
13
);
return
A főprogramban lévő
while
ciklussal pozitív számokat olvasunk be, és ezeket fűzzük föl egy dinamikus listába. Látszik, hogy a listában mindig csak a következő elem számára foglalunk helyet a
malloc
()
függvénnyel. A lista feltöltése után meghívott
list
()
függvény a fölvett elemeket listázza ki.
35. feladat
Fájlkezelés
szerkesztés
A C nyelvben az adatállományokat tartalmuk alapján két csoportra oszthatjuk, szöveges és bináris állományokra. A szöveges állományok olvasható információt tartalmaznak. Sorokból épülnek fel, minden sor végét a CR/LF karakterpár zárja. A bináris állományok byte-okból felépülő adathalmazt jelentenek. A szöveges állomány minden esetben földolgozható, mint bináris állomány, de ez fordítva már nem igaz.
Szöveges állományokban is lehet számokat tárolni. Ebben az esetben a számok, mint karakterek fognak tárolódni. Ha például egy négy jegyű számot szöveges állományban tárolunk, akkor négy byte-nyi helyet foglal el, ha azonban egy bináris állományba tesszük le, akkor csak 2 byte kell. (Természetesen csak akkor, ha int típusú volt)
2.13.1 Fájl előkészítése, lezárása
A fájl kezeléshez tartozó függvények az stdio.h header fájlban vannak leírva. Minden fájlhoz hozzá kell rendelni egy FILE típusú mutatót, amely a memóriában a fájl jellemzőire mutat. Ennek formája:
FILE
fp
// Ez után következhet a fájl megnyitása
fp
fopen
"C:\SZOVEG\NEV.txt"
"mód"
);
Az
fp
mutató értéke
NULL
, ha az állomány megnyitása sikertelen volt. A fizikai fájl névben teljes elérési útvonalat is megadhatunk, ebben az esetben azonban ügyelni kell a
jel használatára:
"C:\SZOVEG\NEV.txt"
helyett
"C:
\\
SZOVEG
\\
NEV.txt"
A mód paraméter azt jelenti, hogy milyen műveleteket akarunk végezni az állománnyal.
Mód Leírás
r (+) Létező fájl megnyitása olvasásra. FILE mutató a fájl elejére áll.
w (+) Új fájl megnyitása írásra. Létező fájl esetén annak tartalma elvész. FILE mutató a fájl elejére áll.
a (+) Fájl megnyitása hozzáírásra. Nyitás után a FILE mutatója a fájl végére áll. Ha a fájl nem létezik, akkor az
fopen
()
létrehozza.
Mindegyik mód jellemző kiegészíthető egy + paraméterrel, ez minden esetben azt jelenti, hogy az állományt olvashatjuk és írhatjuk is. A mód sztringben szoktuk még megadni, hogy milyen állománnyal dolgozunk. t szöveges, b bináris állományt jelent.
Nézzünk egy konkrét példát, melyben egy szöveges állományt megnyitunk írásra és olvasásra:
FILE
fp
fp
fopen
"C:
\\
SZOVEG
\\
NEV.txt"
"r+t"
);
if
fp
==
NULL
printf
"Sikertelen fájl nyitás!"
);
return
A fájl nyitást gyakran szokták az if-en belülre helyezni. Ekkor a szintaktikája a következő:
if
fp
fopen
"C:
\\
SZOVEG
\\
NEV.txt"
"r+t"
)))
printf
"Sikertelen fájl nyitás!"
);
return
Ha a fájlon elvégeztük a megfelelő műveleteket akkor le kell zárni. Ezt az
fclose
fp
függvényhívás valósítja meg. Amennyiben egy fájlban írási műveleteket is végeztünk lezárás előtt célszerű az
fflush
fp
függvényhívás, ez az átmeneti pufferek tartalmát kiírja az állományba, ha még nem történt volna meg.
2.13.2 Szöveges állományok
Szöveges állományokkal kapcsolatban leggyakrabban alkalmazott függvényeket az alábbi táblázatban foglaljuk össze. Minden esetben a
FILE
fp
char
ch
string
[]
"Kiírandó szöveg"
char
sz
20
];
definíciókat használjuk
Függvény Leírás
ch
fgetc
fp
Beolvas egy karaktert a fájlból
fputc
ch
fp
Kiír egy karaktert a fájlba
fgets
sz
strlen
string
fp
Az
sz
változóba beolvas egy sztringet
fputs
string
fp
string
változó tartalmát kiírja a fájlba
fscanf
fp
"konv"
valt
Hasonló a
scanf
()
-hez, eltérés az első paraméterben
fprintf
fp
"konv"
valt
Hasonló a
printf
()
-hez, eltérés az első paraméterben
Az alábbiakban két egyszerű programot mutatunk be a szöveges állományok kezelésére:
#include
#include
#include
int
main
()
char
FILE
fp
int
clrscr
();
if
fp
fopen
"C:
\\
Fajlok
\\
p.txt"
"wt"
)))
fprintf
stderr
"Nem sikerült megnyitni az állományt"
);
return
-1
for
10
++
fprintf
fp
"%4d"
);
fflush
fp
);
fclose
fp
);
if
fp
fopen
"C:
\\
Fajlok
\\
p.txt"
"rt"
)))
fprintf
stderr
"Nem sikerült megnyitni az állományt"
);
return
-1
while
feof
fp
))
fscanf
fp
"%d"
);
printf
"%d"
);
fclose
fp
);
while
((
getch
())
!=
13
);
return
A példában egész értékeket írunk ki egy szöveges állományba, majd visszaolvassuk azokat. A visszaolvasásánál a fájl vége jelölésére az
feof
fp
függvényt használjuk. Ez a fájl végére érve vesz föl
NULL
értéket.
A másik példa egy állomány nevét kéri be, majd karakterenként kilistázza a képernyőre.
#include
#include
int
main
()
char
nev
25
];
FILE
fp
char
clrscr
();
printf
"Állomány neve: "
);
scanf
"%24s"
nev
);
clrscr
();
fp
fopen
nev
"rt"
);
if
fp
==
NULL
printf
"Nem lehet megnyitni a(z) %s állományt
\n
nev
);
else
while
((
fgetc
fp
))
!=
EOF
putchar
);
fclose
fp
);
return
2.13.3 Bináris állományok
A bináris állományokat byte-onként vagy blokkonként kezelhetjük. A byte-onkénti kezeléshez jól használható az előző részben leírt
fgetc
()
és
fputc
()
függvénypáros. A blokkonkénti kezelést pedig az
fread
()
és az
fwrite
()
függvényekkel végezhetjük el.
Használatukat a következő példában figyelhetjük meg.
#include
#include
#include
int
main
()
char
FILE
bf
int
10
10
},
counter
sv
clrscr
();
if
bf
fopen
"C:
\\
Fajlok
\\
adat.dat"
"w+b"
)))
printf
"Sikertelen fájl nyitás"
);
return
-1
fwrite
sizeof
int
),
10
bf
);
rewind
bf
);
counter
fread
sv
sizeof
int
),
bf
);
printf
"%4d"
sv
);
fseek
bf
sizeof
int
),
SEEK_SET
);
/* A 4. elemre áll */
counter
fread
sv
sizeof
int
),
bf
);
printf
"%4d"
sv
);
fseek
bf
sizeof
int
),
SEEK_CUR
);
/* A rákövetkezőre áll */
counter
fread
sv
sizeof
int
),
bf
);
printf
"%4d"
sv
);
fclose
bf
);
while
((
getch
())
!=
13
);
return
Nézzük először az
fwrite
()
függvényt. A függvény első paramétere a kiírandó változó neve, második paramétere az adott struktúrához tartozó blokkméret, harmadik paraméter a kiírandó elemek darabszáma, negyedik pedig az állomány mutatója.
Az
fread
()
függvény hasonlóképpen paraméterezett. Eltérés abban van hogy beolvasáskor nyilván a változó címét kell megadni a függvénynek, a másik három paraméter ugyanaz. Természetesen mindkét függvénynek van visszatérési értéke is. Mindkettőnél a sikeresen kiírt, illetve beolvasott blokkok számát adja vissza. Ez az érték használható a sikertelen beolvasás, illetve kiírás figyelésére. Ha a fenti programban a
counter
változó értéke nem egyenlő eggyel, akkor az azt jelenti, hogy nem sikerült beolvasni adatot az állományból. Ezeknek a változóknak a megadása nem kötelező, mint a fenti programból is látszik az
fwrite
()
függvényt enélkül hívtuk meg.
A programban van még egy újdonság, az állományban való pozicionálás. Erre az
fseek
()
függvényt használjuk. Paraméterei: első paraméter a FILE mutató, második a blokkméret és egy egész szám szorzata (ennyiedik elemre fogunk állni), harmadik pedig azt mutatja meg, hogy mihez viszonyítjuk a pozicionálást.
SEEK_CUR
esetén a pillanatnyi pozícióhoz,
SEEK_SET
esetén pedig a fájl elejéhez. Vigyázzunk azonban ennek az alkalmazásával, az indexelés most is nullával kezdődik, az első elemtől, ennek a
SEEK_SET
esetén lehet jelentősége. A pillanatnyi pozícióval pedig az a helyzet, hogy amikor egy adat beolvasása megtörtént, akkor a FILE mutató rögtön eggyel tovább lép, tehát, ha a szomszédos elemre akarunk lépni, akkor természetesen az
fseek
()
-ben 0-t kell adnunk második argumentumként.
36., 37., 38. feladat
A szöveges képernyő kezelése
szerkesztés
A Turbo C a képernyőt grafikus és szöveges üzemmódban is működtetheti. Mindkét üzemmódban nagyon sok függvény segít változatosabbá tenni a képernyőt. Ebben a fejezetben a szöveges üzemmód függvényeiről lesz szó.
Ha ezeket a függvényeket használni szeretnénk, akkor a program elején a következő header fájlokra lesz szükségünk:
#include
#include
#include
Valójában a képernyőt kezelő függvények az elsőben vannak deklarálva, de képernyőn sztringekkel és karakterekkel dolgozunk, ezért szinte mindig szükségünk van a másik kettőre is. A sztring kezelő függvényekről korábban már volt szó ezt még ebben a fejezetben kiegészítjük néhány konverziós függvénnyel, és a gyakrabban használt karakter kezelő függvényeket is fölsoroljuk.
2.14.1 Szöveges üzemmódban gyakran használt függvények
Név Leírás Név Leírás
cprintf() Formázott kivitel, hasonló a
printf
()
-hez clreol() sor törlése az aktuális kurzor pozíciótól
cputs() egy string kivitele a képernyőre gotoxy() a képernyő adott pontjába mozgatja a kurzort
putch() egy karakter kivitele window() ablak definiálása
getche() egy a karakter beolvasása a billentyűzetről és kiírása a képernyőre textcolor() szöveg színe
clrscr() képernyő törlése textbackground háttér színe
Ha a képernyőn egy ablakot hozunk létre, akkor onnantól kezdve minden koordinátát az ablakhoz képesti relatív értékekkel kell megadnunk. Ha azt akarjuk, hogy az ablak színe más legyen, mint a környezeté, akkor a háttérszín beállítása után törölnünk kell a képernyőt.
Az aktív ablakra vonatkozó információkat is lekérdezhetjük. Ehhez a rendszer egy a conio.h-ban definiált struktúrát bocsát rendelkezésünkre
struct
text_info
unsigned
char
winleft
/* az ablak bal felső sarka x koord */
unsigned
char
wintop
/* az ablak bal felső sarka y koord */
unsigned
char
winright
/* az ablak jobb alsó sarka x koord */
unsigned
char
winbottom
/* az abla jobb alsó sarka y koord */
unsigned
char
attribute
/* a szöveg attribútuma szín+háttér együtt */
unsigned
char
normattr
/* normál attribútum */
unsigned
char
currmode
/* aktuális mód: */
unsigned
char
screenheight
/* a képernyő magassága */
unsigned
char
screenwidth
/* a képernyő szélessége */
unsigned
char
curx
/* a kurzor aktuális pozíciója x koord */
unsigned
char
cury
/* a kurzor aktuális pozíciója y koord */
};
Attól, hogy a struktúrában karakterekként vannak létrehozva az egyes tagok nyugodtan végezhetünk velük matematikai műveleteket, de az is lehetséges, hogy egy egész típusú változóhoz rendeljük hozzá a lekérdezett értékeket. Ha ezt a struktúrát használatba szeretnénk venni, akkor szükség van a
struct
text_info
info
gettextinfo
info
);
definíciókra is. Ezután már minden további nélkül használható a
info
screenwidth
típusú hivatkozás.
Nézzünk egy példa programot a képernyő kezelésére. Az alábbi program az aktuális szöveges képernyő közepére rajzol egy 40x20-as ablakot. Ebbe 5 menüpontot ír ki, és közülük beolvas egyet. A program futásának csak akkor van vége, ha tényleg létező pontot olvastunk be.
#include
#include
#include
#include
#include
#define SZ 20
#define M 10
char
menu_pontok
][
15
"Első pont"
"Második pont"
"Harmadik pont"
"Negyedik pont"
"V É G E"
};
char
cim
[]
" M E N Ü P O N T O K "
char
ala
[]
"-----------------------"
char
val
[]
"Kérem válasszon: "
int
main
()
char
int
xj
yj
xb
yb
int
char
valasz
struct
text_info
info
gettextinfo
info
);
textbackground
BLACK
);
clrscr
();
xb
info
screenwidth
SZ
yb
info
screenheight
xj
xb
SZ
yj
yb
window
xb
yb
xj
yj
);
textbackground
YELLOW
);
textcolor
BLUE
);
clrscr
();
gotoxy
20
strlen
cim
);
cprintf
cim
);
gotoxy
20
strlen
cim
);
cputs
ala
);
for
++
gotoxy
12
);
cprintf
"%2d. %s"
menu_pontok
]);
do
gotoxy
12
-3
);
cprintf
val
);
valasz
getche
();
clreol
();
while
isdigit
valasz
||
valasz
'5'
||
valasz
'1'
);
while
((
getch
())
!=
13
);
return
Tanulmányozzuk át figyelmesen a programot! Vegyük észre, hogy szöveg kiírására a
cputs
()
ugyanolyan alkalmas, minta
cprintf
()
! A
valasz
beolvasásakor találunk egy olyan függvényt, mely az eddigiekben még nem szerepelt,
isdgit
valasz
, visszatérési értéke nem nulla, ha a
valasz
értéke számjegy.
2.14.2 Képernyő mentése és visszatöltése
Egy jól használható lehetősége a szöveges képernyőkezelő függvényeknek az ablak teljes egészének, vagy egy részletének elmentése, majd visszatöltése a memóriába. A következő program erre mutat egy példát:
#include
char
puffer
4096
];
int
main
void
int
clrscr
();
for
<=
20
++
cprintf
"Sorszáma %d
\r\n
);
gettext
80
25
puffer
);
gotoxy
25
);
cprintf
"Képernyőtörlés gombnyomásra..."
);
getch
();
clrscr
();
gotoxy
25
);
cprintf
"Az előző tartalom visszatöltése..."
);
getch
();
puttext
80
25
puffer
);
gotoxy
25
);
cprintf
"Program vége..."
);
getch
();
return
Ehhez a
gettext
()
és a
puttext
()
függvényeket használjuk. Egy billentyű lenyomására várakozik a
getch
()
függvény. Figyeljük meg a puffer méretét! Ha a 80x25=2000 byte-ból indulunk ki, akkor úgy tűnik, mintha kétszer akkora területet foglaltunk volna le, ez azonban nincs így, mert nemcsak azt kell tudnunk, hogy mi jelenik meg a képernyő egy adott pontján, hanem azt is, hogy milyen színű háttéren és milyen karakterszínnel. Ezért minden egyes hely tárolásához 2 byte-ra van szükség. 1 byte a tartalom, 1 byte az attribútum.
Az attribútum byte fölépítése:
2.14.3 Néhány karaktert tesztelő függvény
Minden függvény paramétere
char
típusú változó, és mindegyik visszatérési értéke
int
Függvény Leírás Függvény Leírás
isalnum betű (’A’-’Z’,’a’-’z’) vagy számjegy (’0’-’9’) isxdigit hexadecimális számjegy (’0’-’9’,’A’-’F’,’a’-’f’)
isalpha betű (’A’-’Z’,’a’-’z’) isprint nyomtatható karakter
isascii alsó byte-ja 0 és 127 közé esik islower kisbetű
iscntrl vezérlőkarakter isspace szóköz, CR, LF, tab (vizsz., függ.) lapdobás
isdigit számjegy (’0’-’9’) isupper nagybetű
isgraph nyomtatható karakter, de nem szóköz ispunct elválasztó karakter, az összes nyomtatható karakter a betűk, számok és szóköz nélkül.
jelentése: a függvény visszatérési értéke nem nulla, ha c értéke
2.14.4 Karaktert átalakító függvények
Függvény Leírás
toascii
()
ASCII karakterré alakít
toupper
()
Az angol kis betűket nagy betűkké alakítja
tolower
()
Az angol nagybetűket kis betűkké alakítja
2.14.5 Néhány konverziós függvény
Függvény példával, leírással:
float
char
str
"12345.67"
atof
str
);
// Sztringet konvertál át lebegőpontos értékké, sikertelen esetén 0-val tér vissza
int
char
str
"12345.67"
atoi
str
);
// Sztringet konvertál át egész számmá, sikertelen esetén 0-val tér vissza
long
char
lstr
"98765432"
atol
lstr
);
// Sztringet konvertál át hosszú egész számmá, sikertelen esetén 0-val tér vissza
char
string
"87654321"
endptr
unsigned
long
lnumber
lnumber
strtoul
string
endptr
10
);
// Sztringet egésszé alakít, az utolsó szám a számrendszer alapszáma (2-36), endptr a hibára muatat
char
string
"87654321"
endptr
long
lnumber
lnumber
strtol
string
endptr
10
);
// Az előzőhöz hasonló, csak a visszatérési érték típusa más
int
number
12345
char
string
25
];
itoa
number
string
10
);
// Egész számot sztringgé konvertál, utolsó jegy az alapszám
char
string
25
];
long
value
123456789L
ltoa
value
string
10
);
// Hosszú egészet alakít sztringgé
unsigned
long
lnumber
3123456789L
char
string
25
];
ultoa
lnumber
string
10
);
// Előjel nélküli hosszú egészet alakít sztringgé
39., 40. feladat
A grafika programozása
szerkesztés
A grafikus függvénycsomagban jól használható függvények csoportja áll rendelkezésünkre ahhoz, hogy egyszerűbb pixelgrafikus ábrákat készítsünk. Valójában minden alakzat, amit a képernyőn megjelenítünk, a
putpixel
()
függvényre támaszkodik, de a rajzolás megkönnyítése érdekében sok grafikus primitív áll rendelkezésünkre (egyenes, kör téglalap, ellipszis, ív stb. rajzolása). Ha a grafikus függvényeket használni akarjuk, akkor be kell emelni graphics.h header fájlt. Grafika programozásánál gyakran használjuk a matematikai függvényeket is, ezért általában az elején hozzátesszük a programhoz a math.h header-t is.
2.15.1 Koordináta-rendszer
Mielőtt hozzáfognánk a grafika programozásához meg kell ismernünk a rendszer által alkalmazott grafikus koordináta-rendszert.
A matematikában alkalmazott koordináta-rendszer ettől egy kicsit eltér. Számunkra az lenne a természetes, ha az origó a képernyő középpontjában lenne, és az y értékek alulról fölfelé növekednek. Ha ezt a koordináta-rendszert szeretnénk használni, akkor szükség van egy koordináta transzformációra.
void
trafoxy
int
int
+=
getmaxx
()
getmaxy
()
Tehát ha a matematikai koordinátákkal akarunk dolgozni, akkor számoláskor azokat használjuk, majd ábrázoláskor a fenti függvénnyel áttranszformáljuk azokat képernyő koordinátákká. A függvény hívásának módja:
trafoxy
);
Ha egy grafikus függvény szögekkel dolgozik, akkor azt fokokban kell megadni, a matematikában megszokott körüljárás szerint.
2.15.2 Grafika indítása
Mielőtt grafikai függvényeket használnánk elő kell készítenünk a képernyőt. Ennek módját mutatjuk meg az alábbiakban.
#include
#include
#include
int
main
()
int
Gd
Gm
Hiba
Gd
DETECT
initgraph
Gd
Gm
"c:
\\
progra~1
\\
bc
\\
bgi"
);
Hiba
graphresult
();
if
Hiba
cprintf
"Grafikus hiba: %s"
grapherrormsg
Hiba
));
exit
);
rectangle
100
100
60
40
);
getch
();
closegraph
();
return
DETECT
segítségével földerítjük az aktuális monitor jellemzőit, majd inicializáljuk a grafikát, az idézőjelek között a BGI fájlok helyét kell megadnunk a programnak, ezen állományok segítik a képernyő grafikus üzemmódban való kezelését. A
graphresult
()
függvény jelzi, ha valami miatt nem sikerült az áttérés grafikus módra. Ha minden rendben volt következhetnek a grafikai függvényhívások, (itt ezt a
rectangle
()
függvény jelzi), ha végeztünk, akkor pedig a
closegraph
();
függvénnyel lezárjuk a grafikát.
2.15.3 Gyakran használt függvények
Az alábbiakban egy táblázatban soroljuk föl a leggyakrabban használt grafikus függvényeket, a teljesség igénye nélkül. (A függvénykönyvtárban közel 80 grafikus függvény van)
Függvény Leírás
setcolor(c) A rajzolás színét állítja be c 0-15 közötti érték, vagy színkonstans
setbkcolor(c) A háttér színét állítja be, c ua., mint előbb
setlinestyle(ls,pt,th) ls értéke a vonalstílusára utal:
SOLID_LINE, DOTTED_LINE, CENTER_LINE, DASHED_LINE, USERBIT_LINE ebben az esteben kerül értelmezésre a pt ez egy 16 bites minta, ezt ismétli a vonalon
th a vonalvastagság, NORM_WIDTH, THICK_WIDTH
setfillstyle(pt,c) A kitöltési mintát adja meg pt-ben, c-ben pedig a színt.
EMPTY_FILL, SOLID_FILL, LINE_FILL, LTSLASH_FILL, SLASH_FILL, BKSLASH_FILL, LTBBKSLASH_FILL? HATCH_FILL, XHATCH_FILL,INTERLEAVE_FILL, WIDE_DOT_FILL, CLOSE_DOT_FILL, USER_FILL
moverel(dx,dy) A grafikus kurzort a dx,dy koordinátáit dx-szel, dy-nal változtatja
moveto(x,y) A grafikus kurzort az (x,y) pontba mozgatja
x=getx();y=gety() A grafikus kurzor koordinátái
putpixel(x,y,c) c színnel rajzol egy képpontot
line(x1,y1,x2,y2) A két pontot egyenessel köti össze
linerel(dx,dy) A dx,dy-nal változtatott ponba húz egyenest az aktuális pontból
lineto(x,y) Az (x,y) pontba húz egyenest az aktuális pontból
drawpoly(n,poly[]) Egy poligont rajzol, ha zárt akkor csúcspontjainak száma n-1, különben n, a poly[] tömbben kell fölsorolni a csúcsok koordinátáit, x1,y1,x2,y2,…sorrendben, zárt sokszög esetén az első koordinátáknak egyezniük kell az utolsókkal.
rectangle(x1,y1,x2,y2) Téglalap x1,y1 bal felső, x2,y2 jobb alsó csúcs koordinátái
arc(x,y,ksz,vsz,r) Körív, középpontja x,y, sugara r, az ívhosszat ksz-tő vsz-ig fokokban.
circle(x,y,r) Kör az előző paraméterezéssel.
ellipse(x,y,ksz,vsz,rx,ry) Egy ellipszist, vagy annak egy ívét rajzolja, rx szélességének, ry magasságának fele.
bar(x1,y1,x2,y2) Kitöltött téglalap.
bar3d(x1,y1,x2,y2,m,t) Hasáb 45 fokos axonometriában m a mélység, ha t!=0, akkor a fedőlap is festett.
pieslice(x,y,ksz,vsz,r) Mint az arc, csak ez kitöltött körcikk lesz
floodfill(x,y) Kitölt egy zért területet, melynek x,y belső pontja.
cleardevice() Törli a grafikus képernyőt.
outtext(text) Szöveget ír a képernyőre az aktuális kurzorpozíciótól
ottextxy(x,y,text) A szöveget az x,y koordinátájú ponttól kezdi.
settextstyle(f,d,s) A szöveg stílusát állíthatjuk be vele. f a karakter stílusa
DEFAULT_FONT, TRPLEX_FONT, SMALL_FONT, SANS_SERIF_FONT, GOTHIC_FONT
d=0 esetén balról jobbra, 1 esetén felülről lefelé ír
s a karakter méretét jelenti, ha 1 akkor 8x8 pixel, 2 esetén 16x16 pixel…
41., 42., 43., 44., 45., 46. feladat
Makrók
szerkesztés
A makrók a forráskódba beépített szövegeket jelentenek a C nyelv esetében. Makrók segítségével egyszerű, gyakran alkalmazott műveleteket oldhatunk meg függvények megírása nélkül. C nyelvi makrókat a
#define
direktíva után adhatunk meg. Ezt a direktívát használtuk már korábban konstansok létrehozására.
Az így létrehozott konstansok futásidőben már nem változtathatók. A fordítónak az az első dolga, hogy a forrásnyelvi állományt átadja az előfeldolgozónak. Az előfeldolgozó feladata, hogy a
#define
után talált szövegeket behelyettesítse a forrásprogram azon részébe, ahol hivatkozás történt rájuk. Az előfeldolgozónak lehet, hogy többször is végig kell menni a listán, mivel a makrók minden további nélkül egymásba ágyazhatók
2.16.1 Függvényszerű makrók
A definíció általános alakja:
#define azonosító(paraméterek) (helyettesítő szöveg paraméterekkel)
A makró hívása:
azonosító
argumentumok
Nézzünk meg konkrétan néhány függvényszerű makrót!
#include
#include
#define MIN(a,b) ( (a)>(b)?(b):(a))
#define ABS(x) ( (x)<0?(-(x)):(x))
#define HA_KICSI(x) (((x)>='a') && ((x)<='z'))
#define NAGY(x) (HA_KICSI(x)?(x)-'a'+'A':(x))
int
main
()
char
'f'
int
10
20
int
-3
clrscr
();
printf
"|%d|=%d
\n
ABS
));
printf
"%d, %d közül %d a kisebb
\n
MIN
));
printf
"%c"
NAGY
));
getch
();
return
Első látásra föltűnhet, hogy a makródefinícióban látszólag fölösleges helyeken használunk zárójelezést. Mi értelme van annak például, hogy az
ABS
makróban a
után az
-et zárójelbe tesszük? Induljunk ki abból, hogy az előfeldolgozó csak egyszerű szöveghelyettesítést végez. Azaz, ha a makrót meghívjuk az
értékkel, akkor az
helyébe
-t fog írni. Mi a helyzet, ha a makrót az
a+1
értékkel hívjuk meg? ha zárójelben van az
, akkor nincs gond
–(a+1)
kerül behelyettesítésre, ha azonban elhagyjuk a zárójelet, akkor a
–a+1
szöveg íródik be, amiről könnyen látható, hogy nem egyezik meg az
a+1
ellentettjével. Tehát függvényszerű makrók írása esetén nagyon fontos a zárójelezés. Inkább legyen fölösleges zárójel, mint hibás működés.
Egy másfajta alkalmazása a függvényszerű makróknak, mikor az általuk átadott érték tokenizálódik , vagy pedig az átadott érték szövegként kerül behelyettesítésre. Ezekre az esetekre mutat példát az alábbi program.
#include
#include
#define KIIR(a) printf("%d\n", (x##a))
#define SZOVEG(x) printf("\n"#x"\n")
#define SZAM(x,f) printf("\n"#x"=%"#f"\n", x)
int
main
()
int
x1
x2
x3
x4
13
int
13
clrscr
();
KIIR
);
KIIR
);
KIIR
);
KIIR
);
SZOVEG
Ezt
írd
ki
);
SZAM
);
getch
();
return
KIIR
()
makró bármilyen olyan változónak kiírja az értéket, melynek a neve x-szel kezdődik. A név további részét argumentumként kell átadni.
SZOVEG
makró egyszerűen kiírja az átadott szöveget. Figyeljük meg, hogy híváskor nem kell a szöveget idézőjelek közé tenni, azt a műveletet a változó neve elé kitett # végzi el.
SZAM
()
makró egy változó nevét és értékét írja ki olyan formátumban, amilyet a második paramétere előír.
A lap eredeti címe: „
Kategóriák
Lapok elavult source címkékkel
Címlapok
C/C++
Programozás C nyelven
Új téma nyitása
US