You are on page 1of 288

C programnyelv

Kedves Kollegina, Kollga! A jegyzetet nnek ksztettem azrt, hogy referencia anyaga legyen a Programnyelv s a Programfejleszts trgyakhoz. Szeretnm a segtsgt ignybe venni abbl a clbl, hogy a jegyzet minl pontosabb, megbzhatbb legyen. pp ezrt arra krem, ha az olvass sorn valamilyen magyartalansgba, nem elgsges magyarzatba vagy uram bocs' hibba tkzne, jelezze vissza nekem! Tnykedst elre megksznm. Gyr, 2003. februr (B609) Tel.: (96) 503400/3254 Bauer Pter Varjasi Norbert e-mail: bauer@sze.hu varjasin@sze.hu

BEVEZETS S ALAPISMERETEK

1 BEVEZETS
A Szchenyi Istvn Egyetem klnfle informatika szakjai s szakirnyai C programnyelvi jegyzetignyt hivatott kielgteni ez a jegyzet. Az olvasrl felttelezi, hogy tisztban van a szmtstechnikai alapfogalmakkal [1]. Alapos strukturlt programozsi ismereteket szerzett, s jratos az alapvet algoritmikus elemekben [2]. Jl ismeri a PASCAL programnyelvet s fejleszt krnyezetet [3]. Magyarn ismer, s kezel ilyen fogalmakat, mint: Adatok s adattpusok. Konstansok, vltozk s azonostk. Vezrlsi szerkezetek: szekvencia, szelekci s iterci. Tmbk s sztringek (karakterlncok). Programszerkezeti elemek: eljrsok, fggvnyek s programmodulok. Lncolt adatszerkezetek: listk s fk. Fjlok stb. A C nyelvet tervezje, Dennis Ritchie, a Bell Laboratriumban fejlesztette ki az 1970es vek vgn [4], s a UNIX opercis K&R rendszer programnyelvnek sznta. Ezt a vltozatot jelli az brn a K&R. A C nyelv ezt kveten praktikussga miatt szles krben elterjedt. Sokan ANSI C ksztettek sokfle C fordtt sajt, vagy krnyezetk ignyeinek megfelelen. A soksznsgben amerikai nemzeti szabfordt vnnyal (ANSI) teremtettek rendet az 1980as vek vgn [5]. Az ANSI C szabvnyt aztn Eurpban (ISO) is elfogadtk nhny vvel ksbb. Az brbl ltszik, hogy az ANSI C bvtette a K&R C halmazt. Tovbbi trtneti ttekintshez a [4] s az [5] bevezet rszeit ajnljuk!

C programnyelv

Az brn a legbvebb C halmaz a fordt. Ha valamikor is valamilyen gondunk lenne azzal, hogy egy konkrt C utasts, mdost stb. megfelele az ANSI C szabvnynak, akkor fordts eltt kapcsoljuk be az integrlt programfejleszt rendszer egy menpontjval az ANSI C kompatibilis fordtst! A C ltalnos cl programnyelv, mely tmrsgrl, hatkonysgrl, gazdasgossgrl s portabilitsrl (hordozhatsgrl) ismert. Nem tartalmaz tl sok vezrlsi szerkezetet. Bsges viszont az opertorkszlete, s tbb adattpus meglte jellemzi. Jl hasznlhat teht mszaki tudomnyos, vagy akr adatfeldolgozsi problmk megoldsra. A C elg alacsony szint hardver kzeli programnyelv is egyben, hisz tervezje a UNIX opercis rendszert e nyelv segtsgvel ksztette el nhny szz gpi kd utasts felhasznlsval. A C programok gyakran ugyanolyan gyorsak, mint az ASSEMBLER nyelven kszltek, de jval knnyebben olvashatk s tarthatk karban. Jegyzetnkben nem kvnunk elmerlni konkrt integrlt programfejleszt rendszerek, opercis rendszerek s processzorok rszleteinek taglalsban. Teljes ltalnossgban azonban mg sem clszer a dolgokrl beszlni, mert akkor ilyeneket kne mondani, mint: Kpezzk az opercis rendszernek megfelel vgrehajthat fjlt! Futtassuk a vgrehajthat fjlt az opercis rendszerben! Ehelyett rgztsk azt, hogy fogalmainkkal az IBM PC kompatibilis szemlyi szmtgpek terletn maradunk! Erre a gpcsaldra is rengeteg cg ksztett C fordtt (compiler). Itt llapodjunk meg kt f gyrtnl: a Borlandnl s a Microsoftnl! Az integrlt programfejleszt keretrendszer legyen menvel irnythat, s ne parancssori paramterknt megadott kapcsolkkal kelljen vezrelni a fordtt s a kapcsol szerkesztt (linker). Az opercis rendszer szmunkra jobbra csak olyan szempontbl rdekes, hogy legyen karakteres szabvnyos bemenete (standard input), s ltezzen karakteres szabvny kimenete (standard output), valamint szabvnyos hibakimenete (standard error output). A szabvny bemenet alaprtelmezs szerint a billentyzet, a kimenetek viszont a karakteres zemmd kpernyre, vagy a karakteres konzol ablakba dolgoznak. A karakteres kperny, vagy konzol ablak felbontsa termszetesen vltoztathat, de mi minden pldnl felttelezzk a 25 sorszor 80 oszlopot! A szabvny kimeneteken a mindenkori aktulis pozcit kurzor jelzi.

BEVEZETS S ALAPISMERETEK

2 JELLSEK
Figyelem felkelts. Valamely kvetkeztets levonsa az eddigiekbl. Esetleg: merre tallhatk tovbbi rszletek a krdses tmval kapcsolatban. Lexiklis ismeretek taglalsa. Valamely folyamat pontosabb rszletei. Egy fogalom preczebb defincija. Valamilyen arnylag knnyedn elkvethet, de nehezen lokalizlhat hiba. Egy alapvet, gy nevezett kl szably.
Forrsprogramok s kperny tartalmak szvege.

Valamilyen konkrtummal helyettestend szintaktikai egysg. Kulcssz vagy valamilyen azonost. A fogalom els elfordulsnak jellsre szolgl. A megoldand feladatokat gy jelltk.

C programnyelv

3 ALAPISMERETEK
3.1 Forrsprogram Els kzeltsben induljunk ki abbl, hogy a C program (a forrsprogram) fjlazonostjban C kiterjesztssel rendelkez, ASCII kd szvegfjl, mely elllthat, ill. mdosthat akrmilyen ASCII kd szvegszerkesztvel, vagy a programfejleszt rendszer beptett szvegszerkesztjvel. Az ASCII kd szvegfjl sorokbl ll. Egy sorban a szveg sorbeli karaktereinek ASCII kdjai kvetkeznek rendre az egyms utni bjtokban. A sorhoz vgl mg kt, a soremelst ler bjt tartozik, melyekben egy soremels (Line Feed 10) s egy kocsi vissza (Carriage Return 13) vezrl karakter van. Vigyzni kell ASCII kd szvegfjlban a decimlisan 31 rtk bjt hasznlatval, mert ez fjlvg jelzs a legtbb opercis rendszerben! Ksztsk el els C programunkat, s mentsk el PELDA1.C nven!
/* PELDA1.C */ #include <stdio.h> void main(void){ printf(Ez egy C program!\n); }

Fordtsuk le a programot, szerkesszk meg a vgrehajthat fjlt, s futtassuk le! A mindenki ltal gyantott vgeredmny az a kpernyn, hogy megjelenik a szveg, s a kvetkez sor elejn villog a kurzor:
Ez egy C program! _

3.2 Fordts A fordt sikeres esetben a forrsprogrambl egy vele azonos nev (OBJ kiterjeszts) trgymodult llt el, PELDA1.C fordt
1. bra: Fordts

PELDA1.OBJ

s zeneteket jelentet meg tbbek kzt a hibkrl. A hibazenetek legalbb ktszintek: (fatlis) hibazenetek s

6 figyelmeztet zenetek.

BEVEZETS S ALAPISMERETEK

A (fatlis) hibkat, melyek jobbra szintaktikai jellegek, mindig kijavtja a programoz, mert korrekcijuk nlkl nem kszti el a trgymodult a fordt. A figyelmeztet zenetekkel azonban, melyek sok esetben a legslyosabb problmkat jelzik, nem szokott trdni, mert a fordt ltrehozza a trgymodult, ha csak figyelmeztet zenetekkel zrul a fordts. A PELDA1.C programunk els sora megjegyzs (comment). A megjegyzs rsszablya ltszik a sorbl, azaz: /* karakter prral kezddik s */ karakter prral vgzdik. A megjegyzs tbb forrssoron t is tarthat, minden sorba is rhat egy, st akr egy soron bell tbb is megadhat a szintaktikai egysgek kztt. Egyetlen tilos dolog van: a megjegyzsek nem gyazhatk egymsba!
/* Ez a befoglal megjegyzs eleje. /* Itt a begyazott megjegyzs. */ Ez meg a befoglal megjegyzs vge. */

Vegyk szre, hogy brmely hibs program rgtn hibtlann vlik, ha /*ot tesznk az elejre, s a zr */t elfelejtjk megadni a tovbbi forrsszvegben! A fordtt egybeptettk egy specilis elfeldolgozval (preprocessor), mely az igazi fordts eltt elhagyja a forrsszvegbl a megjegyzseket, vgrehajtja a neki szl direktvkat, s ezeket is elhagyja a forrsszvegbl. elfeldolgoz PELDA1.C fordt
2. bra: Fordts pontosabban

PELDA1.OBJ

Az elfeldolgoz direktvk egy sorban helyezkednek el, s #tel kezddnek. Pontosabban # kell, legyen a sor els nem fehr karaktere. Fehr karakterek a szkz, a soremels, a lapdobs karakter, a vzszintes s a fggleges tabultor karakter. Meg kell emlteni, hogy a meg-

C programnyelv

jegyzs is fehr karakternek minsl. A fehr karakterek szolglhatnak szintaktikai egysgek elvlasztsra, de a felesleges fehr karaktereket elveti a fordt. A PELDA1.C programunk msodik sora egy #include direktva, melynek hatsra az elfeldolgoz megkeresi s betlti a paramter szvegfjlt a forrsszvegbe, s elhagyja belle ezt a direktvt. A pillanatnyilag betltend szvegfjl, az stdio.h, H kiterjeszts. Az ilyen kiterjeszts szvegfjlokat C krnyezetben fejfjloknak (header) nevezik. A fejfjl egy tmval kapcsolatos adattpusok, konstansok definciit s a vonatkoz fggvnyek jellemzit tartalmazza. Fedezzk fel, hogy az stdio a standard input output rvidtsbl szrmazik, s gy lnyegben a szabvny kimenetet s bemenetet kapcsoltuk programunkhoz. Nem volt mg sz arrl, hogy a paramter fjlazonost <> jelek kztt ll! A <> jel pr tjkoztatja az elfeldolgozt, hogy milyen knyvtrakban keresse a szvegfjlt. A programfejleszt rendszer menpontjai kzt van egy, melynek segtsgvel megadhatk a fejfjlok (include fjlok) keressi tjai. <fjlazonost> alak paramter hatsra az #include direktva csak a programfejleszt rendszerben elrt utakon keresi a fjlt, s sehol msutt. PELDA1.C programunk harmadik s negyedik sora a main (f) fggvny defincija. A fggvnydefinci szintaktikai alakja:
tpus fggvnynv(formlisparamterlista) { fggvnytest }

A visszatrsi rtk tpusa void, mely kulcssz ppen azt jelzi, hogy a fggvnynek nincs visszaadott rtke. A fggvnynv main. C krnyezetben a main az indt program. A formlisparamterlista mindig ()ben van. Pillanatnyilag itt is a void kulcssz lthat, ami e helyt azt rgzti, hogy nincs formlis paramter. A fggvnytestet mindig {}ben, gy nevezett blokk zrjelekben, kell elhelyezni. PASCAL programoznak csak annyi emltend, hogy a { a BEGIN, s a } az END. A forrsprogram tbb forrsmodulban (forrsfjlban) is elhelyezhet. Vgrehajthat program ltrehozshoz azonban valamelyik forrsmodulnak tartalmaznia kell a maint. Az indt program a vgrehajthat

BEVEZETS S ALAPISMERETEK

program belpsi pontja is egyben. Ez az a hely, ahova a memriba trtnt betlts utn tadja a vezrlst az opercis rendszer. Az indt programnak termszetesen a vgrehajthat program igazi indtsa eltt el kell ltnia nhny ms feladatot is, s a programoz ltal rt mainbeli fggvnytest csak ezutn kvetkezik. A gyrtk az indt programot rendszerint trgymodul (OBJ) alakjban szoktk rendelkezsre bocstani. A PELDA1.C programban a main fggvny teste egyetlen fggvnyhvs. A fggvnyhvs szintaktikja:
fggvnynv(aktulisparamterlista)

A fggvnynv a printf, mely fggvny az aktulis paramtert megjelenteti a szabvny kimeneten. A ()ben ll aktulisparamterlista egytag, s momentn egy karakterlnc konstans. A karakterlnc konstans rsszablya ltszik: idzjelek kz zrt karaktersorozat. A karakterlnc konstanst a memriban meg kpzeljk gy el, hogy a fordt a szveg karaktereinek ASCII kdjait rendre elhelyezi egymst kvet bjtokban, majd vgl egy tiszta zrustartalm (minden bitje zrus) bjttal jelzi a karakterlnc vgt! A pldabeli karakterlnc konstans vgn azonban van egy kis furcsasg: a \n! Ha valaki ttanulmnyozza az ASCII kdtblt, akkor lthatja, hogy a lehetsges 256 kdpozci nem mindegyikhez tartozik karakterkp. Emltsk csak meg a szkz (32) alatti kdpozcikat, ahol az gy nevezett vezrl karakterek is elhelyezkednek! Valahogyan azt is biztostania kell a programnyelvnek, hogy ezek a karakterek, ill. a karakterkp nlkli kdpozcik is megadhatk legyenek. A C programnyelvben erre a clra az gynevezett escape szekvencia (escape jelsorozat) szolgl. Remlhetleg vilgoss vlt az elz okfejtsbl, hogy az escape szekvencia helyfoglalsa a memriban egyetlen bjt! Az escape szekvencia \ jellel kezddik, s ezutn egy karakter kvetkezik. A teljessg ignye nlkl felsorolunk itt nhnyat!

C programnyelv Escape szekvencia Jelents \b \n \r \t \ \\ \0 \ooo visszatrls (back space) soremels vagy j sor (line feed) kocsi vissza (carriage return) vzszintes tabultor (horizontal tab) egyetlen karakter egyetlen \ karakter karakterlnc zr bjt, melynek minden bitje zrus az ok oktlis szmok

Vegyk szre, hogy ha idzjelet kvnunk a karakterlnc konstansba rni, akkor azt csak \ mdon tehetjk meg! Ugyanez a helyzet az escape szekvencia kezdkaraktervel, mert az meg csak megkettzve kpez egy karaktert! Lssuk mg be, hogy a \ooo alakkal az ASCII kdtbla brmely karaktere lerhat! Pldul a \012 azonos a \nnel, vagy a \060 a 0 szmjegy karakter. A printf fggvnyhvs utn ll ; utastsvg jelzs. PELDA1.C programunk utols fehr foltja a printf, mely egyike a szabvnyos bemenet s kimenet fggvnyeinek. 3.3 Kapcsolszerkeszts (link) A gyrtk a szabvny bemenet, kimenet s ms tmacsoportok fggvnyeinek trgykdjt statikus knyvtrakban (LIB kiterjeszts fjlokban) helyezik el. Nevezik ezeket a knyvtrakat futsidej knyvtraknak (run time libraries), vagy szabvny knyvtraknak (standard libraries) is. A knyvtrfjlokbl a szksges trgykdot a kapcsolszerkeszt msolja hozz a vgrehajthat fjlhoz. Lssuk brn is a szerkesztst! PELDA1.OBJ indt program (OBJ) knyvtrak (LIB)
3. bra: Kapcsolszerkeszts

kapcsol szerkeszt

PELDA1.EXE

10

BEVEZETS S ALAPISMERETEK

A programfejleszt keretrendszerben a sikeres mkdshez bizonyosan be kell lltani a statikus knyvtrfjlok (library) keressi tjait. Azt is meg kell adni termszetesen, hogy hova kerljenek a fordts s a kapcsolszerkeszts sorn keletkez kimeneti fjlok. 3.4 Futtats A vgrehajthat fjl a parancssorbl azonostjnak begpelsvel indthat. Valsznleg a programfejleszt rendszer menjben is van egy pont, mellyel az aktulis vgrehajthat fjl futtathat. Tbbnyire ltezik a hrom lpst (fordts, kapcsolszerkeszts s futtats) egyms utn megvalst egyetlen menpont is. Ha programunk kimenete nem lthat a kpernyn, akkor egy msik menpont segtsgvel t kell vltani a felhasznli kpernyre (user screen), vagy aktuliss kell tenni a futtatott vgrehajthat fjl programablakt. 3.5 Tblzat ksztse Ksztsk el a kvetkez forinteur tszmtsi tblzatot! Egy eur pillanatnyilag legyen 244 forint 50 fillr!
Forint 100 200 300 . . . 1000 Eur 0.41 0.82 1.23 . . . 4.09

Adatstruktra: A vltozk a vals tpus euron kvl, mind egszek. also lesz a tartomny als hatra, felso a tartomny fels rtke, lepes a lps-

kz, s ft a ciklusvltoz. Algoritmus: Deklarljuk a vltozkat! Elltjuk ket az eurotl eltekintve kezdrtkkel. Megjelentetjk a tblzat fejlc sort s az alhzst. Mkdtetjk a ciklust, mg ft <= felso. A ciklusmagban kiszmtjuk az aktulis euro rtket, megjelentetjk az sszetartoz ft euro rtkprt, s vgl lptetjk az ft ciklusvltozt. Ksztsk el a programot!
/* PELDA2.C: Forint-eur tszmtsi tblzat */ #include <stdio.h>

C programnyelv
void main(void){ int also, felso, lepes, ft; /* Deklarcik */ float euro; also = 100; /* Vgrehajthat utastsok */ felso = 1000; lepes = 100; ft = also; printf( " Forint| Eur\n" "---------+---------\n"); while(ft <= felso){ /* Begyazott (bels) blokk */ euro = ft / 244.5; printf("%9d|%9.2f\n", ft, euro); ft = ft + lepes; } }

11

A PELDA2.Cbl kitnen ltszik a C fggvny szerkezete, azaz az gy nevezett blokkszerkezet: Elbb a deklarcis utastsok jnnek a blokkbeli vltozkra. A C szigor szintaktikj nyelv: elzetes deklarci nlkl nem hasznlhatk benne a vltozk, s kivtel nlkl deklarlni kell minden hasznlatos vltozt! Aztn a vgrehajthat utastsok kvetkeznek. A blokkszerkezet deklarcis s vgrehajthat rszre bontsa a C ben szigor szintaktikai szably, azaz egyetlen vgrehajthat utasts sem keveredhet a deklarcis utastsok kz, ill. a vgrehajthat utastsok kztt nem helyezkedhet el deklarcis utasts. Termszetesen a vgrehajthat utastsok kz begyazott (bels) blokkban a szably jra kezddik. Vegyk szre a pldaprogramunk vgn elhelyezked begyazott vagy bels blokkot! Figyeljk meg a main kt els sorban, hogy a deklarcis utasts szintaktikja:
tpus azonostlista;

Az azonostlista azonostk sorozata egymstl vesszvel elvlasztva. Megfigyelhet mg, hogy az azonostk kpzshez az angol bc beti hasznlhatk fel! Foglalkozzunk kicsit a tpusokkal! Az int (integer) 16, vagy 32 bites, alaprtelmezs szerint eljeles (signed), fixpontos belsbrzols egsz tpus. Vegyk, mondjuk, a 16 bites esetet! A legnagyobb, mg brzolhat pozitv egsz binrisan s decimlisan:

12

BEVEZETS S ALAPISMERETEK

0111 1111 1111 11112 = 215 1 = 32767 A negatv rtkek kettes komplemens alakjban troltak. A legkisebb, mg brzolhat rtk gy: 1000 0000 0000 00002 = 215 = 32768 Eljeltelen (unsigned) esetben nincsenek negatv rtkek. A szmbrzolsi hatrok zrus s 1111 1111 1111 11112 = 216 1 = 65535 kzttiek. Az elzk 32 bites esetre ugyanilyen knnyedn levezethetek! A float (floating point) 4 bjtos, lebegpontos belsbrzols vals tpus, ahol a mantissza s eljele 3 bjtot, s a karakterisztika eljelvel egy bjtot foglal el. Az brzolsi hatrok: 3.4*10-38 3.4*10+38. Ez a mantissza mret 6 7 decimlis jegy pontossgot tesz lehetv. Nhny programfejleszt rendszer a lebegpontos knyvtrakat (LIB) csak akkor kapcsolja be a kapcsolszerkeszt ltal keressnek alvethet knyvtrak kz, ha a programban egyltaln igny jelentkezik valamilyen lebegpontos brzols, vagy mvelet elvgeztetsre. A PELDA2.C vgrehajthat rsznek els ngy utastsa rtkads. Ki kell azonban hangslyozni, hogy a Cben nincs rtkad utasts, csak hozzrendels opertor, s a hozzrendelsekbl azrt lesz utasts, mert ;t rtunk utnuk. A hozzrendelsre rgtn visszatrnk! Vegyk szre elbb az egsz konstans rsszablyt! Elhagyhat eljellel kezddik, s ilyenkor pozitv, s ezutn az egsz szm jegyei kvetkeznek. A fejlc sort s az alhzst egyetlen printf fggvnyhvssal valstottuk meg. Ltszik, hogy a tblzat oszlopait 9 karakter szlessgre vlasztottuk. Figyeljk meg, hogy a pontos pozcionlst segtend a fejlc sort s az alhzst a printfben kt egyms al rt karakterlnc konstansknt adtuk meg! A C fordt a csak fehr karakterekkel elvlasztott karakterlnc konstansokat egyesti egyetlen karakterlnc konstanss, s gy a pldabeli printfnek vgl is egyetlen paramtere van. A PELDA2.Cben az elltesztel ciklusutasts kvetkezik, melynek szintaktikja:

C programnyelv
while(kifejezs) utasts

13

A kifejezs aritmetikai, azaz szmrtk. Az elltesztel ciklusutasts hatsra lpsenknt a kvetkez trtnik: 1. Kirtkeli a fordt a kifejezst. Ha hamis (zrus), akkor vge a ciklusnak, s a while-t kvet utasts jn a programban. 2. Ha a kifejezs igaz (nem zrus), akkor az utasts vgrehajtsa, s aztn jbl az 1. pont kvetkezik. Vilgos, hogy a kifejezs rtknek valahogyan vltoznia kell az utastsban, klnben a ciklusnak soha sincs vge. Az utasts llhat tbb utastsbl is, csak {}be kell tenni ket. A {}ben ll tbb utastst sszetett utastsnak nevezik. Az sszetett utasts szintaktikailag egyetlen utastsnak minsl. A PELDA2.Cben a while kifejezse relci. A relcijelek a szoksosak: kisebb (<), kisebb egyenl (<=), nagyobb (>) s nagyobb egyenl (>=). A relci kt lehetsges rtke: az igaz s a hamis logikai rtk. A Cben azonban nincsen logikai adattpus, gy az igaz az 1 egsz rtk, s a zrus a hamis. A pldabeli bels blokk els s utols utastsa hozzrendels, melynek szintaktikai alakja:
objektum = kifejezs

A hozzrendels opertor (mveleti jel) bal oldaln valami olyan objektumnak kell llnia, ami rtket kpes felvenni. A szaknyelv ezt mdosthat balrtknek nevezi. Pldnkban az sszes hozzrendels bal oldaln egy vltoz azonostja ll. Az = jobb oldaln meghatrozhat rtk kifejezsnek (jobbrtknek) kell helyet foglalnia. A hozzrendels hatsra a kifejezs rtke - esetlegesen az objektum tpusra trtnt konverzi utn - fellrja az objektum rtkt. Az egsz konstrukci rtke az objektum j rtke, s tpusa az objektum tpusa. A legutbbi mondat azt clozza, hogy ha a hozzrendels kifejezs rsze, akkor ez az rtk s tpus vesz rszt a kifejezs tovbbi kirtkelsben. A begyazott blokkbeli kt hozzrendels kifejezse aritmetikai. Az aritmetikai mveleti jelek a szoksosak: sszeads (+), kivons (), szorzs (*) s az oszts (/). Vegyk szre, hogy az eddigi printf fggvnyhvsainknak egyetlen karakterlnc konstans paramtere volt, mely vltozatlan tartalommal jelent meg a karakteres kpernyn! A bels blokkbeli printfben viszont

14

BEVEZETS S ALAPISMERETEK

hrom aktulis paramter van: egy karakterlnc konstans, egy int s egy float. A gond ugye az, hogy az int s a float paramter rtkt megjelentets eltt karakterlncc kne konvertlni, hisz binris bjtok kpernyre vitelnek semmifle rtelme nincs! A printf els karakterlnc paramtere karakterekbl s formtumspecifikcikbl ll. A karakterek vltozatlanul jelennek meg a kpernyn, a formtumspecifikcik viszont meghatrozzk, hogy a printf tovbbi paramtereinek rtkeit milyen mdon kell karakterlncc alaktani, s aztn ezt hogyan kell megjelentetni. A formtumspecifikci % jellel indul s tpuskarakterrel zrul. A formtumspecifikcik s a printf tovbbi paramterei balrl jobbra haladva rendre sszetartoznak. St ugyanannyi formtumspecifikci lehet csak, mint ahny tovbbi paramter van. Felsorolunk nhny tpuskaraktert a kvetkez tblzatban: Tpuskarakter d f c s A hozztartoz paramter tpusa egsz tpus lebegpontos egy karakter karakterlnc decimlis egszknt tizedes trt alakjban karakterknt karakterlncknt Megjelents

A formtumspecifikci pontosabb alakja:


%<szlessg><.pontossg>tpuskarakter

A <>be ttel az elhagyhatsgot hivatott jelezni. A szlessg annak a meznek a karakteres szlessgt rgzti, amiben a karakterlncc konvertlt rtket alaprtelmezs szerint jobbra igaztva, s balrl szkzfeltltssel kell megjelentetni. Ha a szlessget elhagyjuk a formtumspecifikcibl, akkor az adat a szksges szlessgben jelenik meg. Maradjunk annyiban pillanatnyilag, hogy a pontossg lebegpontos esetben a tizedes jegyek szmt hatrozza meg! Lssuk be, hogy a "%9d|%9.2f\n" karakterlnc konstansbl a %9d s a %9.2f formtumspecifikcik, mg a | s a \n sima karakte-

C programnyelv

15

rek! Vegyk szre, hogy a nagy pozcionlgats helyett tblzatunk fejlc sornak s alhzsnak megjelentetst gy is rhattuk volna:
printf("%9s|%9s\n---------+---------\n", "Forint", "Eur");

Foglalkoznunk kell mg egy kicsit a mveletekkel! Vannak egyoperandusos (opertor operandus) s ktoperandusos (operandus opertor operandus) mveletek. Az egyoperandusos opertorokkal kevs problma van. Az eredmny tpusa tbbnyire egyezik az operandus tpusval, s az rtke az operandus rtkn vgrehajtva az opertort. Pldul: vltoz. Az eredmny tpusa a vltoz tpusa, s az eredmny rtke a vltoz rtknek 1szerese. Problmk a ktoperandusos mveleteknl jelentkezhetnek, s hanyagoljuk el a tovbbiakban az eredmny rtkt! Ha ktoperandusos mveletnl a kt operandus tpusa azonos, akkor az eredmny tpusa is a kzs tpus lesz. Ha a kt operandus tpusa eltr, akkor a fordt a rvidebb, pontatlanabb operandus rtkt a hosszabb, pontosabb operandus tpusra konvertlja, s csak ezutn vgzi el a mveletet. Az eredmny tpusa termszetesen a hosszabb, pontosabb tpus. A ft/244.5 osztsban a ft egsz tpus s a 244.5 konstans lebegpontos. A mvelet elvgzse eltt a ft rtkt lebegpontoss alaktja a fordt, s csak ezutn hajtja vgre az osztst. Az eredmny teht ugyancsak lebegpontos lesz. Ezt implicit tpuskonverzinak nevezik. Vegyk szre kzben a vals konstans rsszablyt is! Elhagyhat eljellel kezddik, amikor is pozitv, s aztn az egsz rsz jegyei jnnek. Aztn egy tizedespont utn a trt rsz szmjegyei kvetkeznek. A problma a PASCAL programoz szmra egszek osztsnl jelentkezik, hisz egszek osztsnak eredmnye is egsz, s nincs semmifle maradkmegrzs, lebegpontos talakts! Ttelezzk fel, hogy 50 fillrrel cskkent az eur rfolyama! Alaktsuk csak t az euro=ft/244.5 hozzrendelst euro=ft/244re, s rgtn lthatjuk, hogy az eredmnyekben sehol sincs trt rsz! Felvetdik a krds, hogyan lehetne ilyenkor a helyes rtket meghatrozni? A vlasz: explicit tpusmdosts segtsgvel, melynek szintaktikai alakja:
(tpus)kifejezs

16

BEVEZETS S ALAPISMERETEK

Hatsra a kifejezs rtkt tpus tpusv alaktja a fordt. A konkrt esetben az oszts legalbb egyik operandust floatt kne mdostani, s ugye akkor a ktoperandusos mveletekre megismert szably szerint a msik operandus rtkt is azz alaktan a fordt a mvelet tnyleges elvgzse eltt, azaz:
euro = (float)ft / 244;

Megoldand feladatok: Ksztsen programot, mely a kperny 21 sorszor 21 oszlopos terletn a csillag karakter felhasznlsval megjelentet: Egy keresztet a 11. sor s 11. oszlop feltltsvel! A ftlt (bal fels sarokbl a jobb alsba ment)! A mellktlt (a msik tlt)! Egyszerre mindkt tlt, azaz egy X-et! A forinteur tszmtsi tblzatot elkszt PELDA2.C megoldsunkkal az a baj, hogy tl sok vltozt hasznlunk. Knnyen belthatjuk, hogy az fttl eltekintve a tbbi vltoz nem is az, hisz a program futsa alatt nem vltoztatja meg egyik sem az rtkt! Ksztsnk PELDA3.C nven egy jobb megoldst!
/* PELDA3.C: Forint-eur tszmtsi tblzat */ #include <stdio.h> void main(void){ int ft; printf("%9s|%9s\n---------+---------\n", "Forint", "Eur"); for(ft=100; ft<=1000; ft=ft+100) printf("%9d|%9.2f\n", ft, ft/244.5); }

PELDA3.C programunkban kt j dolog lthat. Az egyik a


for(<initkifejezs>; <kifejezs>; <lptetkifejezs>) utasts

elltesztel, iteratv ciklusutasts, melynek vgrehajtsa a kvetkez lpsek szerint trtnik meg: 1. A fordt vgrehajtja az initkifejezst, ha van. Az elhagyhatsgot most is <> jelekkel szemlltetjk! 2. Kirtkeli a kifejezst. Ha hamis (zrus), akkor vge a ciklusnak, s a for-t kvet utasts jn a programban. Lthat, hogy a szintaktika szerint ez a kifejezs is elhagyhat. Ilyenkor 1nek (igaznak) minsl.

C programnyelv

17

3. Ha a kifejezs igaz (nem zrus), akkor az utasts vgrehajtsa jn. Az utasts most is lehetne sszetett utasts is! 4. Vgl az ugyancsak elhagyhat lptetkifejezs, majd jbl a 2. pont kvetkezik. Ha a for utastst while-lal szeretnnk felrni, akkor azt gy tehetjk meg:
<initkifejezs>; while(kifejezs) { utasts; <lptetkifejezs>; }

A szintaktikai szablyt sszefoglalva: a for-bl akr mindegyik kifejezs is elhagyhat, de az els kettt zr pontosvesszk nem! A PELDA3.C programbeli msik j dolog az, hogy a printf aktulis paramtereknt kifejezs is megadhat. A PELDA3.C ugyan sokat rvidlt, de ezzel a megoldssal meg az a problma, hogy tele van varzskonstansokkal. Ha megvltoztatnnk tszmtsi tblzatunkban a tartomny als, ill. fels hatrt, mdostannk a lpskzt, vagy az eur rfolyamot, akkor ennek megfelelen t kellene rnunk varzskonstansainkat is. Az ilyen trogats 8 soros programnl knnyen, s remlhetleg hibamentesen megvalsthat. Belthat azonban, hogy nagymret, esetleg tbb forrsfjlbl ll szoftver esetben, amikor is a varzskonstansok rengeteg helyen elfordulhatnak, ez a mdszer megbzhatatlan, vagy legalbb is nagyon hibagyans. A C a problma megoldsra a szimbolikus konstansok, vagy ms megnevezssel: egyszer makrk, hasznlatt javasolja. A metdus a kvetkez: 1. Definilni kell a benne hasznlatos szimbolikus llandkat egy helyen, egyszer, a forrsfjl elejn. 2. Aztn a programban vgig a konstansok helyett szisztematikusan a szimbolikus konstansokat kell hasznlni. A vltoztats is nagyon egyszerv vlik gy: a megvltozott konstans rtkt egy helyen t kell rni, s a tbbi felhasznlsa automatikusan mdosul a kvetkez fordtsnl. A szimbolikus lland a
#define azonost helyettestszveg

18

BEVEZETS S ALAPISMERETEK

elfeldolgoz direktvval definilhat. A szimbolikus lland tulajdonkppen az azonost a direktva helytl a forrsszveg vgig van rvnyben. Az elfeldolgoz kihagyja a direktvt a forrsszvegbl, majd vgigmegy rajta, s az azonost minden elfordulst helyettest szvegre cserli. Lssuk a medvt!
/* PELDA4.C: Forint-eur tszmtsi tblzat */ #include <stdio.h> #define ALSO 100 /* A tartomny als hatra */ #define FELSO 1000 /* A tartomny fels rtke */ #define LEPES 100 /* A lpskz */ #define ARFOLYAM 244.5 /* Ft/eur rfolyam */ void main(void){ int ft; printf( "%9s|%9s\n---------+---------\n", "Forint", "Eur"); for(ft=ALSO; ft<=FELSO; ft=ft+LEPES) printf("%9d|%9.2f\n", ft, ft/ARFOLYAM); }

Szoks mg klnsen tbb forrsmodulos esetben a #define direktvkat (s mg ms dolgokat) kln fejfjlban elhelyezni, s aztn ezt minden forrsfjl elejn #include direktvval bekapcsolni. Ksztsk csak el ezt a varicit is!
/* BEGEND.H: Fejfjl az tszmtsi tblhoz */ #include <stdio.h> #define ALSO 100 /* A tartomny als hatra */ #define FELSO 1000 /* A tartomny fels rtke */ #define LEPES 100 /* A lpskz */ #define ARFOLYAM 244.5 /* Ft/eur rfolyam */ #define begin { /* {} helyett begin-end! */ #define end } #define then /* if utastsban then! */ #define LACI for /* Kulcsszavak tdefinilsa nem javasolt! */ /* PELDA5.C: Forint-eur tszmtsi tblzat */ #include "BEGEND.H" void main(void) begin int ft; printf("%9s|%9s\n---------+---------\n", "Forint", "Eur"); LACI(ft=ALSO; ft<=FELSO; ft=ft+LEPES) printf("%9d|%9.2f\n", ft, ft/ARFOLYAM); end

C programnyelv

19

Vegyk szre, hogy az #include direktva fjlazonostja nem <> k, hanem k kztt ll! Ennek hatsra az elfeldolgoz a megadott azonostj fjlt elszr az aktulis mappban abban a knyvtrban, ahol az a .C fjl is elhelyezkedik, melyben a #include direktva volt keresi. Ha itt nem tallja, akkor tovbbkeresi a programfejleszt rendszerben belltott utakon. 3.6 Bemenet, kimenet A kiss lergott csont forinteur tszmtsi tblzatos pldnkban nem volt bemenet. Megtanultuk mr, hogy a szabvny bemenet s kimenet hasznlathoz be kell kapcsolni az STDIO.H fejfjlt:
#include <stdio.h>

Alaprtelmezs szerint szabvny bemenet (stdin) a billentyzet, s szabvny kimenet (stdout) a kperny. A legtbb opercis rendszerben azonban mindkett tirnythat szvegfjlba is. Egy karaktert olvas be a szabvny bemenetrl az int getchar(void); fggvny. Ezt aztn balrl zrus feltltssel intt tpusmdostja, s visszaadja a hvnak. Azt, ahogyan az elbb a getchart lertuk, fggvny prototpusnak nevezik. A fggvny prototpus teljes formai informcit szolgltat a szubrutinrl, azaz rgzti: a fggvny visszatrsi rtknek tpust, a fggvny nevt, paramtereinek szmt, sorrendjt s tpust. A getchar fjl vgn, vagy hiba esetn EOFot szolgltat. Az EOF az STDIO.H fejfjlban definilt szimbolikus lland:
#define EOF (1)

Tekintsk csak meg az STDIO.H fejfjlban! Nzegets kzben vegyk azt is szre, hogy a fejfjl tele van fggvny prototpusokkal. Mr csak az a krds maradt, hogy mi a fjlvg a szabvny bemeneten, ha az a billentyzet? Egy opercis rendszertl fgg billentykombinci: Ctrl+Z vagy Ctrl+D. A paramter karaktert kiviszi a szabvny kimenet aktulis pozcijra az

20 int putchar(int k);

BEVEZETS S ALAPISMERETEK

s sikeres esetben vissza is adja ezt az rtket. A hibt pp az jelzi, ha a putchar szolgltatta rtk eltr ktl. Ksztsnk programot, ami a szabvny bemenetet tmsolja a szabvny kimenetre!
/* PELDA6.C: Bemenet msolsa a kimenetre */ #include <stdio.h> void main(void){ int k; printf("Bemenet msolsa a kimenetre:\n" "Gpeljen Ctrl+Z-ig sorokat!\n\n"); k=getchar(); while(k!=EOF){ if(k!=putchar(k)) printf("Hiba a kimeneten!\n"); k=getchar(); } }

Fogalmazzuk meg minimlis elvrsainkat egy programmal szemben! A szoftver indulsakor jelezze ki, hogy mit csinl! Ha valamilyen eredmnyt kzl, akkor azt lssa el tjkoztat szveggel, mrtkegysggel stb.! Ha valamit bekr, akkor tjkoztasson rla, hogy mit kell megadni, milyen egysgben stb.! A bemenet ellenrzend! A hibs adat helyett a hiba okt esetleg kijelezve azonnal krjen jat a program! A <, <=, >, >= relcijelekrl mr sz volt! A Cben != a nem egyenl opertor s == az egyenl mveleti jel. Az == s a != radsul a tbbi relcinl eggyel alacsonyabb prioritsi szinten foglal helyet. Kifejezs kirtkelse kzben elbb a magasabb priorits mveletet vgzi el a fordt, s csak aztn kvetkezik az alacsonyabb. Vigyzat! Az egyenl relcit az egyms utn rt, kt egyenlsg jel jelzi. Az egyetlen egyenlsg jel a hozzrendels opertor! A ktirny szelekci szintaktikai alakja:
if(kifejezs) utasts1 <else utasts2>

Az elhagyhatsgot most is a <> jelzi. Ha a kifejezs igaz (nem zrus), akkor utasts1 vgrehajtsa kvetkezik. Ha a kifejezs hamis (zrus) s

C programnyelv

21

van else rsz, akkor az utasts2 kvetkezik. Mindkt utasts sszetett utasts is lehet. A PELDA6.C megoldsunk tlzottan PASCAL z. Cben programunk utols 5 sort gy kne megrni:
while((k=getchar())!=EOF) if(k!=putchar(k)) printf("Hiba a kimeneten!\n");

A while kifejezse egy nem egyenl relci, melynek bal oldali operandusa egy kln zrjelben ll hozzrendels. Elbb a hozzrendels jobb oldalt kell kirtkelni. Lssuk csak sorban a kirtkels lpseit! 1. Meghvja a getchar fggvnyt a fordt. 2. A visszakapott rtkkel fellrja k vltoz rtkt. 3. A getchartl kapott rtket hasonltja EOFhoz. A kifejezsbl a hozzrendels krli kln zrjel nem hagyhat el, mert a hozzrendels alacsonyabb priorits mvelet a relcinl. Ha mgis elhagynnk, akkor a kirtkels sorn a fordt: 1. Meghvn elbb a getchar fggvnyt. 2. A visszatrsi rtket hasonltan EOFhoz. Teht kapna egy logikai igaz (1), vagy hamis (0) rtket! 3. A k vltoz felvenn ezt az 1, vagy 0 rtket. Figyeljk meg a PELDA6.C futtatsakor, hogy a getchar a bemenetrl olvasott karaktereket az opercis rendszer billentyzet pufferbl kapja! Emlkezznk csak vissza! A parancssorban a begpelt szveget szerkeszthetjk mindaddig, mg Entert nem nyomunk. A billentyzet pufferben lev karakterek teht csak akkor llnak a getchar rendelkezsre, ha a felhasznl lettte az Enter billentyt. Ksztsnk programot, mely fjlvgig leszmllja, hogy hny numerikus karakter, fehr karakter, ms egyb karakter s sszesen hny karakter rkezett a szabvny bemenetrl! Megoldsunkban az sszes vltoz egsz tpus. k tartalmazza a beolvasott karaktert. A num, a feher s az egyeb szmllk. Az algoritmus:

22

BEVEZETS S ALAPISMERETEK

Deklarljuk a vltozkat, s az sszes szmllt lssuk el zrus kezdrtkkel! Jelentessk meg a program cmt, s tjkoztassunk a hasznlatrl! Mkdtessk addig a ciklust, mg EOF nem rkezik a bemenetrl! A ciklusmagban hromirny szelekci segtsgvel el kell gazni a hrom kategria fel, s ott meg kell nvelni eggyel a megfelel szmllt! A ciklus befejezdse utn megjelentetendk a szmllk rtkei megfelel tjkoztat szvegekkel, s az is, hogy sszesen hny karakter rkezett a bemenetrl!
/* PELDA7.C: A bemenet karaktereinek leszmllsa kategrinknt */ #include <stdio.h> void main(void){ short k, num, feher, egyeb; num=feher=egyeb=0; printf("Bemeneti karakterek leszmllsa\n" "kategrinknt EOF-ig, vagy Ctrl+Z-ig.\n"); while((k=getchar())!=EOF) if(k>='0'&&k<='9')++num; else if(k==' '||k=='\n'||k=='\t')++feher; else ++egyeb; printf("Karakter szmok:\n" "----------------\n" "numerikus: %5hd\n" "fehr: %5hd\n" "egyb: %5hd\n" "----------------\n" "ssz: %10ld\n", num, feher, egyeb, (long)num+feher+egyeb); }

Pontostani kell a deklarcis utasts eddig megismert szintaktikjt!


<tpusmdostk> <alaptpus> azonostlista;

Az elhagyhat alaptpus alaprtelmezs szerint int. Az ugyancsak elhagyhat tpusmdostk az alaptpus valamilyen jellemzjt vltoztatjk meg. int tpus esetn: Az egsz alaprtelmezs szerint eljeles (signed), s lehetne mg eljeltelen (unsigned). A signed s az unsigned mdostk egymst kizrak. Kt, egymst kizr hosszmdostval az egsz belsbrzolsa

C programnyelv bizonyosan 16 bites (short), ill. biztos 32 bites (long). Vgl is a klnfle int tpusok mretei gy sszegezhetk: short <= int <= long

23

Vegyk szre, hogy az ismertetett szablyok szerint a short, a short int s a signed short int azonos tpusok. A short s a short int rsmdnl figyelembe vettk, hogy signed az alaprtelmezs. A short felrsakor mg arra is tekintettel voltunk, hogy a meg nem adott alaptpus alaprtelmezse int. Ugyanezek mondhatk el a long, a long int s a signed long int vonatkozsban is. Ugyan a szintaktika azt mutatja, de a deklarcis utastsban a tpusmdostk s az alaptpus egyszerre nem hagyhatk el! Feltve, hogy a, b s c balrtkek, az
a=b=c=kifejezs

rtelmezse megint abbl fakad, hogy a hozzrendels a Cben opertor, azaz:


a=(b=(c=kifejezs))

A fordt jobbrl balra halad, azaz kirtkeli a kifejezst, s visszafel jvet berja az eredmnyt a balrtkekbe. A konstrukci hatsra a fordt gyorsabb kdot is hoz ltre. Ugyanis a
c=kifejezs; b=kifejezs; a=kifejezs;

rsmdnl hromszor kell kirtkelni ugyanazt a kifejezst. Cben a tbbg (N) szelekcira az egyik kdolsi lehetsg:
if(kifejezs1)utasts1 else if(kifejezs2)utasts2 else if(kifejezs3)utasts3 /* . . . */ else utastsN

Ha valamelyik if kifejezse igaz (nem zrus) a konstrukciban, akkor a vele azonos sorszm utasts vgrehajtsa kvetkezik, majd a konstrukcit kvet utasts jn. Ha minden kifejezs hamis (zrus), akkor viszont utastsN hajtand vgre.

24

BEVEZETS S ALAPISMERETEK

Fedezzk fel a karakter konstans rsszablyt: aposztrfok kztt karakter, vagy escape szekvencia. A karakter konstans belsbrzolsa int, gy az ASCII kd egsz rtknek is minsl kifejezsekben. A PELDA7.Cbl lthat, hogy a logikai s mveletet && jelli, s a logikai vagy opertor a ||. A ktoperandusos logikai opertorok prioritsa alacsonyabb a relciknl, s az && magasabb priorits, mint a ||. Ha a ktoperandusos logikai mvelet eredmnye eldl a bal oldali operandus kirtkelsvel, akkor a C bele sem kezd a msik operandus rtkelsbe. Az s mvelet eredmnye eldlt, ha a bal oldali operandus hamis. A vagy pedig akkor ksz, ha az els operandus igaz. A Cben van inkrementls (++) s dekrementls () egsz rtkekre. Mindkt mvelet egyoperandusos, teht nagyon magas priorits. A ++ operandusa rtkt eggyel nveli meg, s a pedig eggyel cskkenti, azaz:
++vltoz vltoz=vltoz+1 --vltoz vltoz=vltoz1

A problmk ott kezddnek azonban, hogy mindkt mvelet ltezik eltag (prefix) s uttag (postfix) opertorknt is! Foglalkozzunk csak a ++ opertorral! A ++vltoz s a vltoz++ hatsra a vltoz rtke eggyel mindenkpp megnvekedik. Kifejezs rszeknt eltag opertor esetn azonban a vltoz j rtke vesz rszt a tovbbi kirtkelsben, mg uttag mveletnl a vltoz eredeti rtke szmt be. Feltve, hogy a s b egsz tpus vltozk, s b rtke 6:
a = ++b; a = b++; /* a=7 s b=7 */ /* a=7 s b=8 */

Figyeljnk fel r, hogy a PELDA7.C utols printf utastsban hosszmdostk llnak a d tpuskarakterek eltt a formtumspecifikcikban! Ltszik, hogy a h jelzi a printfnek, hogy a formtumspecifikcihoz tartoz aktulis paramter short tpus (2 bjtos), ill. l tudatja vele, hogy a hozztartoz aktulis paramter long (4 bjtos). A megfelel hosszmdostk megadsa a formtumspecifikcikban elengedhetetlen, hisz nem mindegy, hogy a fggvny a verem kvetkez hny bjtjt tekinti a formtumspecifikcihoz tartoznak!

C programnyelv

25

Vegyk azt is szre, hogy a tpusokhoz a mezszlessggel is felkszltnk: a maximlis pozitv short rtk bizonyosan elfr 5 pozcin, s long pedig 10en! Lthat mg, hogy arra is vigyztunk, hogy a hrom maximlis short rtk sszege rszeredmnyknt se csonkuljon! Ezrt az explicit longg mdosts a printf utols paramterben:
(long)num+feher+egyeb

Megoldand feladatok: Ksztsen programokat a PELDA4.C alapjn a kvetkezkppen: A forint 1000-tl 100ig cskkenjen 100asval! Az eur nvekedjk 1tl 10ig egyesvel! A forint 100tl 2000ig nvekedjen 100asval! Az eredmnyt a kpernyn fejlccl elltva kt oszlop prban oszlopfolytonosan haladva kell megjelentetni. A bal oldali oszlop pr 100zal, a jobb oldali viszont 1100zal kezddjk! A feladat maradjon ugyanaz, mint az elbb, de a megjelentets legyen sorfolytonos. A bal oldali oszlop pr kezddjk 100zal, a jobb oldali viszont 200zal, s mindegyik oszlop prban 200 legyen a lpskz! Maradva a sorfolytonos megjelentetsnl, krjk be elbb a kijelzend oszlop prok szmt ellenrztt inputtal! Az oszlop prok szma 1, 2, 3 vagy 4 lehet. A mg kijelzend fels rtk ennek megfelelen 1000, 2000, 3000 vagy 4000. Az eredmny a kpernyn fejlccl elltva az elrt szm oszlop prban jelenjen meg gy, hogy 100 tovbbra is a lpskz! A forint 100tl 10000ig nvekedjen 100asval! A lista nem futhat el a kpernyrl, azaz fejlccl elltva lapozhatan kell megjelentetni! Ez azt jelenti, hogy elszr kiratjuk a lista egy kperny lapnyi darabjt, majd vrunk egy gombnyomsra. A gomb letsekor produkljuk a lista kvetkez lapjt, s jbl vrunk egy gombnyomsra, s gy tovbb. Legyen ugyanaz a feladat, mint az elz pontban, de a lista a kpernyn fejlccl elltva nem csak elre, hanem elrehtra lapozhatan jelenjen meg! Ksztsen programokat, melyek a szabvny bemenetet EOFig olvassk, s kzben megllaptjk, hogy:

26

BEVEZETS S ALAPISMERETEK

Hny sor volt a bemeneten? A bemenet karakterei kzt a \neket kell leszmllni. Az utols sor persze lehet, hogy nem \nnel vgzdik, hanem EOFfal. Hny sz volt a bemeneten? A sz nem fehr karakterekbl ll. A szavakat viszont egymstl fehr karakterek vlasztjk el. Az utols sz lehet, hogy nem fehr karakterrel zrul, hanem EOFfal. 3.7 Tmbk Ksztsnk programot, mely a szabvny bemenetet olvassa EOF-ig! Megllaptand s kijelzend, hogy hny A, B, C stb. karakter rkezett! A kis s nagybetk kztt nem tesznk klnbsget! A betkn kvli tbbi karaktert tekintsk egy kategrinak, s ezek darabszmt is jelezzk ki! Megoldsunkban az elvalaszto karakteres vltoz, a k, a tobbi s a betu viszont egsz tpus. A k tartalmazza a beolvasott karaktert, s ciklusvltozi funkcikat is ellt. A tobbi s a betu szmllk. A betu annyi elem tmb, mint ahny bet az angol bcben van. A tobbi a betkn kvli tbbi karakter szmllja. Az elvalaszto karakteres vltozra azrt van szksg, mert az eredmny csak kt oszlop pros listaknt kzlhet egy kpernyn. Az algoritmus: Deklarljuk a vltozkat, s a tmbt! A szmllk nullzandk! Az elvalaszto induljon szkz kezdrtkkel! Jelentessk meg a program cmt, s tjkoztassunk a hasznlatrl! Mkdtessk addig a ciklust, mg EOF nem rkezik a bemenetrl! A ciklusmagban hromirny szelekcival el kell gazni hrom kategria fel: nagybet, kisbet s ms karakter. Megnvelend egygyel termszetesen a megfelel szmll! A ciklus befejezdse utn kt oszlop pros tblzatban megjelentetendk a betszmllk rtkei, s vgl egy kln sorban a tbbi karakter kategria szmllja!
/* PELDA8.C: Betszmlls a bemeneten */ #include <stdio.h> #define BETUK 26 /* Az angol bc betszma */ void main(void){ char elvalaszto; /* Listelvlaszt karakter. */ int k, /* Bemeneti kar. s ciklusvltoz. */ tobbi, /* Nem betk szmllja. */ betu[BETUK]; /* Betszmllk. */ tobbi=0; /* Kezdrtk ads. */ for(k=0; k<BETUK; ++k) betu[k]=0;

C programnyelv
elvalaszto=' '; printf( "Bemenet betinek leszmllsa\n" "EOF-ig, vagy Ctrl+Z-ig.\n"); while((k=getchar())!=EOF) /* Nagybetk: */ if(k>='A'&&k<='Z')++betu[k-'A']; /* Kisbetk: */ else if(k>='a'&&k<='z')++betu[k-'a']; /* Ms karakterek: */ else ++tobbi; /* Eredmnyek kzlse: */ printf("\nBet|Darab Bet|Darab\n" "----+----- ----+-----\n"); for(k=0; k<BETUK; ++k){ printf("%4c|%5d%c", k+'A', betu[k], elvalaszto); if(elvalaszto==' ') elvalaszto='\n'; else elvalaszto=' '; } printf("\nTbbi karakter: %5d\n", tobbi); }

27

A char tpus vltoz egyetlen karakter trolsra alkalmas. A char ugyanakkor 8 bites, alaprtelmezs szerint eljeles (signed), fixpontos belsbrzols egsz tpus is 0111 11112 = 27 1 = 127 s 1000 00002 = 27 = 128 brzolsi hatrokkal. Az unsigned char 0 s 255 kztti brzolsi lehetsgekkel rendelkezik. A legtbb programfejleszt rendszerben az unsigned char alaprtelmezsknt is bellthat karakter tpus. A PELDA8.Cbl kitnen ltszik, hogy a tmb defincija
tpus tmbazonost[mret];

alak. Pontosabban a deklarcis utasts azonostlistja nem csak egyszer vltozk azonostibl llhat, hanem tmbazonost[mret] konstrukcik is lehetnek kztk. A tmbdefinciban a mret pozitv, egsz rtk lland kifejezs, s a tmb elemszmt hatrozza meg. lland kifejezs az, aminek fordtsi idben kiszmthat az rtke. A tmb egy elemnek helyfoglalsa tpustl fgg. Az egsz tmb a memriban sszesen
sizeof(tmbazonost) mret*sizeof(tpus)

bjtot ignyel. Pldul 16 bites intet felttelezve a sizeof(betu) 26*sizeof(int) pontosan 52. A magas priorits, egyoperandusos sizeof opertor megadja a mgtte zrjelben ll objektum, vagy tpus ltal elfoglalt bjtok szmt.

28

BEVEZETS S ALAPISMERETEK

A tmb egy elemre val hivatkozst indexes vltoznak is nevezik s szintaktikailag a kvetkez:
tmbazonost[index]

ahol az index nem negatv rtk egsz kifejezs


0 <= index <= mret1

rtkhatrokkal. A tmbindexels Cben mindig zrustl indul, s a legnagyobb mg ltez indexrtk a mret1! Pldul a betu tmbnek ltezik betu[0], betu[1], betu[2], s vgl betu[BETUK1] eleme, s ezek gy helyezkednek el a memriban: betu[0] betu[1] betu[2] ... betu[24] betu[25] Vegyk szre, hogy a betu[0]ban a program az A, a betu[1]ben a B, , s a betu[25]ben a Z karaktereket szmllja! Ttelezzk fel, hogy k rtke 68! Ez ugyebr a D bet ASCII kdja. Ilyenkor a betu[kA] szmll n eggyel. Az A ASCII kdja 65. Teht betu[6865]rl, azaz betu[3] nvelsrl van sz! Figyeljnk fel mg arra, hogy az eredmnyeket kzl ciklusbeli printfben a k+A egsz kifejezs rtkt %c formtumspecifikcival jelentetjk meg, azaz rendre 65t, 66ot, 67et stb. ratunk ki karakteresen, teht At, Bt, Ct stb. ltunk majd. Lssuk mg be, hogy az elvalaszto vltoz rtke szkz s soremels karakter kzt vltakozik, s gy kt betdarab pr kpes megjelenni egy sorban. Teht az elvalaszto vltoz segtsgvel produkljuk a kt oszlop pros eredmnylistt. Listzni csak azt rdemes, ami valamilyen informcit hordoz! Teht a zrus darabszm betk kijelzse teljesen felesleges! Magyarn a for ciklusbeli printfet gy kne mdostani:
if(betu[k]>0) printf("%4c|%5d%c", k+'A', betu[k], elvalaszto);

Megoldand feladatok: Fokozza gy a PELDA8.Cben megoldott feladatot, hogy megszmllja a magyar kezetes kis s nagybetket is! Ksztsen programot, mely a szabvny bemenetet EOFig olvassa! Szmllja meg s jelezze ki, hogy hny 0, 1, 2 stb. karakter rkezik! A

C programnyelv

29

nem numerikus karaktereket tekintse egy kategrinak, s ezek szmt is kzlje! 3.8 Fggvnyek A fggvnyeket tbbflekppen csoportosthatnnk, de a legpraktikusabb gy, hogy: Vannak elre megrtak. Knyvtrakban (.LIB), vagy trgymodulokban (.OBJ) tallhatk, s a kapcsol-szerkeszt kapcsolja be ket a vgrehajthat fjlba. Pldul: a printf, a getchar, a putchar, vagy a main stb. Minden vgrehajthat programban kell lennie egy fggvnynek, az indt programnak (a main-nek), mely az egsz program belpsi pontjt kpezi. Mi rjuk ket. Forrsfjlokban helyezkednek el, s kdjukat a fordt generlja. A nyelv kzponti eleme a fggvny. A ms programozsi nyelvekben szoksos eljrs (procedure) itt explicit mdon nem ltezik, mert a C szellemben az egy olyan fggvny, aminek nincs visszaadott rtke:
void eljrs();

Jelezzk ki egy tblzatban az 1001 s 1010 kztti egsz szmok kbt!


/* PELDA9.C: Kbtblzat */ #include <stdio.h> #define TOL 1001 /* A tartomny kezdete. */ #define IG 1010 /* A tartomny vge. */ long kob(int); /* Fggvny prototpus. */ void main(void){ int i; printf(" Szm|%11s\n-----+-----------\n", "Kbe"); for(i=TOL; i<=IG; ++i) /* Fggvnyhvs. */ printf("%5d|%11ld\n", i, kob(i)); } long kob(int a){ /* Fggvnydefinci. */ return (long)a*a*a; }

A fggvnydefinci s a fggvnyhvs fogalmval megismerkedtnk mr a Kapcsolszerkeszts fejezetben. A fggvnydefinciban van meg a fggvny teste, azaz az a kd, amit a fggvny meghvsakor vgrehajt a processzor. Egy fggvnyre a programban csak egyetlen definci ltezhet, s ennek nem mondhatnak ellent a prototpusok (deklarcik)! A fggvnydefinciban elrt visszaadott rtk tpusnak egyeznie kell ebbl kvetkezleg a programban brhol elfordul, e fggvnyre vonat-

30

BEVEZETS S ALAPISMERETEK

koz prototpusokban (deklarcikban) megadott visszatrsi rtk tpussal. A meghvott fggvny akkor ad vissza rtket a hv fggvnynek a hvs pontjra, ha a processzor kifejezssel elltott return utastst hajt vgre benne. A valamit szolgltat fggvnyben teht lennie kell legalbb egy return kifejezs; utastsnak, s r is kell, hogy kerljn a vezrls. A visszaadott rtk meghatrozatlan, ha a processzor nem hajt vgre return utastst, vagy a return utastshoz nem tartozott kifejezs. A visszaadott rtk tpusa brmi lehet vgl is eltekintve a tmbtl s a fggvnytl. Lehet valamilyen alaptpus, de el is hagyhat, amikor is az alaprtelmezs lesz rvnyben, ami viszont int. Nzzk a return szintaktikjt!
return <kifejezs>;

A fordt kirtkeli a kifejezst. Ha a fggvny visszatrsi tpusa tpus, akkor a kifejezs tpusnak is ennek kell lennie, vagy implicit konverzival ilyen tpusv alaktja a kifejezs rtkt a fordt, s csak azutn adja vissza. Lssuk be, hogy a PELDA9.Cbeli returnben az explicit (long) tpusmdosts nem azrt van, hogy megtakartsuk a kifejezs rtknek visszaads eltti implicit konverzijt! Az igazi ok az, hogy egy 16 bites int kbe nem biztos, hogy elfr az intben! Gondoljunk 1000 kbre, ami 1000000000! Ez jval meghaladja a 32767es felsbrzolsi korltot. Cben az egsz tpusok terletn nincs sem tlcsorduls, sem alulcsorduls! Pontosabban ami tlcsordul, vagy alulcsordul, az mindenfle zenet nlkl elveszik. A fggvnyhvs truhzza a vezrlst a hv fggvnybl a hvottba gy, hogy az aktulis paramtereket is tadja ha vannak rtk szerint. A vezrlst a fggvnytest els vgrehajthat utastsa kapja meg. void visszatrs fggvny blokkjban aztn a vgrehajts addig folytatdik, mg kifejezs nlkli return utasts nem kvetkezik, vagy a fggvny blokkjt zr }-re nem kerl a vezrls. Ezutn a hvsi ponttl folytatdik a vgrehajts. Vegyk szre, hogy a return utasts szintaktikjban az elhagyhat kifejezs a paramter nlkli returnt kvnta jellni! Belthat, hogy a fggvny prototpusnak mindig meg kell elznie a hvst a forrsszvegben. A fordt gy tisztban van a hvs helyn a fggvny paramtereinek szmval, sorrendjvel s tpusval, ill. ismeri a fggvny visszatrsi rtknek tpust is.

C programnyelv

31

A fordt a prototpus ismeretben implicit tpuskonverzit is vgrehajt az aktulis paramter rtkn a fggvnynek trtn tads eltt, ha az aktulis paramter tpusa eltr. Ha nincs prototpus, akkor nincs implicit konverzi, s csak a csoda tudja, hogy mi trtnik az tadott nem megfelel tpus rtkkel. Pldul a kob(3.0) hvs eredmnye zrus, ami remlhetleg kellen szemllteti a prototpus megadsnak szksgessgt. Ha nincs prototpus, akkor a fordt azt felttelezi (teht olyan hvsi kdot generl), hogy a fggvnynek az alaprtelmezs miatt int visszaadott rtke van. Ez ugyebr elgg rdekes eredmnyre vezet void, vagy lebegpontos visszatrs fggvnyek esetben. A nem int visszaadott rtk fggvnyt legalbb deklarni kell a hv fggvnyben! A fggvnydeklarci bemutatshoz trjuk a PELDA9.Ct:
/* PELDA9.C: Kbtblzat */ #include <stdio.h> #define TOL 1001 /* A tartomny kezdete. */ #define IG 1010 /* A tartomny vge. */ void main(void){ int i; long kob(); /* Fggvnydeklarci. */ printf(" Szm|%11s\n-----+-----------\n", "Kbe"); for(i=TOL; i<=IG; ++i) /* Fggvnyhvs: */ printf("%5d|%11ld\n", i, kob(i)); } long kob(int a){ /* Fggvnydefinci. */ return (long)a*a*a; }

Vegyk szre rgtn, hogy deklarcis utastsunk szintaktikja ismt mdosult! Az azonostlista nem csak egyszer vltozk azonostibl s tmbazonost[mret] konstrukcikbl llhat, hanem tartalmazhat
fggvnynv()

alakzatokat is. Termszetesen a teljes fggvny prototpus is berhat a deklrcis utastsba,


long kob(int); /* Fggvnydeklarci. */

de ilyenkor a fggvny prototpus csak ebben a blokkban lesz rvnyben. Lssuk be, hogy az utbbi mdszer nem ajnlhat olyan tbb fggvnydefincibl ll forrsfjlra, ahol a krdses fggvnyt tbb helyrl is meghvjk! Sokkal egyszerbb a forrsszveg elejn megadni egyszer a

32

BEVEZETS S ALAPISMERETEK

prototpust, mint minden t hv fggvnyben kln deklarlni a fggvnyt. A fggvny defincija prototpusnak is minsl, ha megelzi a forrsszvegben a fggvnyhvst.
/* PELDA9.C: Kbtblzat */ #include <stdio.h> #define TOL 1001 /* A tartomny kezdete. */ #define IG 1010 /* A tartomny vge. */ long kob(int a){ /* Fggvnydefinci. */ return (long)a*a*a; } void main(void){ int i; printf(" Szm|%11s\n-----+-----------\n", "Kbe"); for(i=TOL; i<=IG; ++i) /* Fggvnyhvs: */ printf("%5d|%11ld\n", i, kob(i)); }

Cben tilos fggvnydefincin bell egy msikat kezdeni, azaz a fggvnydefincik nem gyazhatk egymsba! Ugyan a Tblzat ksztse fejezetben mr rgztettk a fggvny szerkezett, vagyis a blokkszerkezetet, de itt jra kihangslyozzuk, hogy a fggvnydefinciban elbb a deklarcis utastsok jnnek, s a vgrehajthat utastsok csak ezutn kvetkeznek, s a kt rsz nem keveredhet egymssal. Ebben a fejezetben csak az rtk szerinti hvsrl szltunk, vagyis amikor a formlis paramterek rtkt kapja meg a meghvott fggvny. Van termszetesen nv (cm) szerinti hvs is a Cben, de ezt most mg nem trgyaljuk! 3.9 Prodzsekt Ha a vgrehajthat program forrsszvegt tmnknt, vagy funkcinknt klnkln forrsfjlokban kvnjuk elhelyezni, akkor Cs programfejleszt rendszerekben ennek semmifle akadly sincs. Be kell azonban tartani a kvetkez szablyokat: Egy s csak egy forrsmodulban szerepelnie kell az indt programnak (main). Prodzsektfjlt kell kszteni, melyben felsorolandk a program teljes szvegt alkot forrsfjlok.

C programnyelv

33

Szedjk szt kt forrsmodulra: FOPROG.Cre s FUGGV.Cre, a PELDA9.C programunkat!


/* FOPROG.C: Kbtblzat */ #include <stdio.h> #define TOL 1001 /* A tartomny kezdete. */ #define IG 1010 /* A tartomny vge. */ long kob(int); /* Fggvny prototpus. */ void main(void){ int i; printf(" Szm|%11s\n-----+-----------\n", "Kbe"); for(i=TOL; i<=IG; ++i) /* Fggvnyhvs: */ printf("%5d|%11ld\n", i, kob(i)); } /* FUGGV.C: A fggvnydefinci. */ long kob(int a){ return (long)a*a*a; }

Hozzunk ltre egy j prodzsektet! Soroljuk fel benne, vagy szrjuk bele a kt forrsfjlt, s mentsk el, mondjuk, PRODZSI fjlazonostval! A prodzsektfjl nvadsnl csak arra vigyzzunk, hogy egyetlen benne felsorolt fjl azonostjval se egyezzen meg a neve! Azrt nem konkretizljuk a prodzsektfjl kiterjesztst, mert az programfejleszt rendszerenknt msms lehet! A programfejleszt rendszerben kell lennie olyan menpontoknak, melyekkel j prodzsektet hozhatunk ltre, meglvt tlthetnk be, nyithatunk meg, menthetnk el, trlhetnk, zrhatunk le stb. Betlttt, vagy megnyitott prodzsekt esetn azonban a fejleszt rendszer mindaddig a prodzsekt fordtsval, kapcsolszerkesztsvel s futtatsval foglalkozik, mg nem trljk, nem zrjuk be. Akrmilyen ms forrsfjlokat is nyitogatnnk meg klnfle ablakokban, a programfejleszt rendszer az aktulis prodzsekt bezrsig nem ezek fordtsval, szerkesztsvel, vagy futtatsval foglalkozik. Lssuk a prodzsekt fordtst s kapcsolszerkesztst! FOPROG.C FUGGV.C fordts FOPROG.OBJ FUGGV.OBJ

4. bra: A PRODZSI prodzsekt fordtsa

34 FOPROG.OBJ FUGGV.OBJ indt program (OBJ) knyvtrak (LIB)

BEVEZETS S ALAPISMERETEK

kapcsol szerkeszts

PRODZSI.EXE

5. bra: A PRODZSI prodzsekt kapcsolszerkesztse

Fedezzk fel, hogy a vgrehajthat fjl a prodzsekt nevt kapja meg! A prodzsektet alkot fjlok kztt implicit fggsg van. Ez azt jelenti, hogy a prodzsekt futtatsakor csak akkor trtnik meg a trgymodul alakjban is rendelkezsre ll forrsfjl fordtsa, ha a forrsfjl utols mdostsnak ideje (dtuma s idpontja) ksbbi, mint a vonatkoz trgymodul. A kapcsolszerkeszts vgrehajtshoz az szksges, hogy a trgymodulok, ill. a knyvtrak valamelyiknek ideje ksbbi legyen a vgrehajthat fjlnl. Az implicit fggsg fennll a forrsfjl s a bele #include direktvval bekapcsolt fjlok kztt is. A trgymodult akkor is jra kell fordtani, ha valamelyik forrsfjlba behozott fjl ideje ksbbi a trgymodulnl. Bizonyos programfejleszt rendszereknl elfordulhat, hogy az implicit fggsgi mechanizmust gy kell kln aktivlni (menpont), ill. hogy a forrsfjlok, s a beljk behozott fjlok kzti fggsget explicit mdon kell biztostani.
stdio.h foprog.c fuggv.c foprog.obj fuggv.obj indt prog. knyvtrak PRODZSI.EXE

6. bra: Implicit fggsg a PRODZSI prodzsektnl

A prodzsektfjlban a forrsmodulokon kvl megadhatk trgymodulok (OBJ) s knyvtrak (LIB) fjlazonosti is. A kapcsol szerkeszt a trgymodulokat beszerkeszti a vgrehajthat fjlba. A knyvtrakban pedig fggvnyek trgykdjait fogja keresni. A prodzsektfjlban tulajdonkppen a gyri indt program s a szabvny knyvtrak is kicserlhetek, de ennek pontos megvalstsa mr igazn a programfejleszt rendszertl fgg.

C programnyelv

35

3.10 Karaktertmb s karakterlnc A karaktertmbk defincija a Tmbk fejezetben ismertetettek szerint:


char tmbazonost[mret];

Az egsz tmb helyfoglalsa:


mret*sizeof(char) mret

bjt. A tmbindexels ebben az esetben is zrustl indul s mret1ig tart. A Cben nincs kln karakterlnc (sztring) adattpus. A karakterlncokat a fordtnak s a programoznak karaktertmbkben kell elhelyeznie. A karakterlnc vgt az t tartalmaz tmbben egy zrusrtk bjttal (\0) kell jelezni. Pldul a Karakterlnc karakterlncot gy kell letrolni a tomb karaktertmbben: tomb K a r a k t e r l n c \0 0 1 2 3 4 5 6 7 8 9 10 11 12

Vegyk szre, hogy a karakterlnc els jele a tomb[0]ban, a msodik a tomb[1]ben, s a legutols a tomb[11]ben helyezkedik el, s az ezt kvet tomb[12] tartalmazza a lnczr zrust! Figyeljnk fel arra is, hogy a karakterlnc hossza (12) megegyezik a lezr \0 karaktert magba foglal tmbelem indexvel! Fedezzk mg rgtn fel, hogy a zrus egsz konstans (0) s a lnczr \0 karakter rtke ugyanaz: zrus int tpusban! Hiszen a karakter konstans belsbrzolsa int. A Cben nincs kln karakterlnc adattpus, s ebbl kvetkezleg nem lteznek olyan sztring mveletek sem, mint a karakterlncok egyestse, sszehasonltsa, hozzrendelse stb. Ezeket a mveleteket bjtrlbjtra haladva kell kdolni, vagy fggvnyt kell rni rjuk, mint ahogyan azt a kvetkez pldban bemutatjuk. Ksztsen programot, mely neveket olvas a szabvny bemenetrl EOF ig vagy res sorig! Megllaptand egy fordtsi idben megadott nvrl, hogy hnyszor fordult el a bemeneten! A feladat megoldshoz ksztend

36

BEVEZETS S ALAPISMERETEK

Egy int strcmp(char s1[], char s2[]) fggvny, mely sszehasonltja kt karakterlnc paramtert! Ha egyeznek, zrust ad vissza. Ha az els htrbb van a nvsorban (nagyobb), akkor pozitv, egybknt meg negatv rtket szolgltat. Egy int getline(char s[], int n) fggvny, mely behoz a szabvny bemenetrl egy sort! A sor karaktereit rendre elhelyezi az s karaktertmbben. A befejez soremels karaktert nem viszi t a tmbbe, hanem helyette lnczr \0t r a karakterlnc vgre. A getline msodik paramtere az s karaktertmb mretnl eggyel kisebb egsz rtk, azaz a lnczr zrus nlkl legfeljebb n karaktert trol a tmbben a fggvny. A getline visszatrsi rtke az s tmbben vgl is elhelyezett karakterlnc hossza.
/* PELDA10.C: Nvszmlls */ #include <stdio.h> #define NEV "Jani" /* A szmllt nv. */ #define MAX 29 /* A bemeneti sor maximlis mrete. Most egyben a leghosszabb nv is. */ int getline(char s[],int n); /* Fggvny prototpusok. */ int strcmp(char s1[], char s2[]); void main(void){ int db; /* Nvszmll. */ char s[MAX+1]; /* Az aktulis nv. */ db=0; /* A szmll nullzsa. */ printf( "A(z) %s nv leszmllsa a bemeneten.\nAdjon\ meg soronknt egy nevet!\nProgramvg: res sor.\n",NEV); /* Sorok olvassa res sorig a bemenetrl: */ while(getline(s,MAX)>0) /* Ha a sor pp a NEV: */ if(strcmp(s,NEV)==0) ++db; /* Az eredmny kzlse: */ printf("A nevek kzt %d darab %s volt.\n",db,NEV); } int strcmp(char s1[], char s2[]){ int i; for(i=0; s1[i]!=0&&s1[i]==s2[i]; ++i); return(s1[i]-s2[i]);} int getline(char s[],int n){ int c,i; for(i=0;i<n&&(c=getchar())!=EOF&&c!='\n';++i) s[i]=c; s[i]='\0'; return(i); }

Ha a forrskd egy sort lezr soremels karaktert kzvetlenl \ jel elzi meg, akkor mindkt karaktert elveti a fordt, s a kt fizikai sort egyesti, s egynek tekinti. Az ilyen rtelemben egyestett sorokat mr a msodiktl kezdve folytatssornak nevezik.

C programnyelv A main els printfjt folytatssorral ksztettk el.

37

A kt elksztett fggvny prototpusbl s defincijbl vegyk szre, hogy a formlis paramter karaktertmb (s persze ms tpus tmb is) mret nlkli! Az strcmp megrsakor abbl indultunk ki, hogy kt karakterlnc akkor egyenl egymssal, ha ugyanazon pozciikon azonos karakterek llnak, s radsul a lnczr zrus is ugyanott helyezkedik el. Az i ciklusvltozt zrusrl indtva, s egyesvel haladva, vgigindexeljk az s1 karaktertmbt egszen a lnczr nullig (s1[i]!=0) akkor, ha kzben minden i re az s1[i]==s2[i] is teljeslt. Ebbl a ciklusbl teht csak kt esetben van kilps: Ha elrtnk s1 karaktertmb lnczr zrusig (s1[i]==0). Ha a kt karakterlnc egyazon pozcijn nem egyforma rtk ll (s1[i]!=s2[i]). A visszaadott s1[i]s2[i] klnbsg csak akkor: Zrus, ha s1[i] zrus s s2[i] is az, azaz a kt karakterlnc egyenl. Negatv, ha s1 kisebb (elbbre van a nvsorban), mint s2. Pozitv, ha s1 nagyobb (htrbb van a nvsorban), mint s2. Az strcmpt mr megrtk. Trgykdja benne van a szabvny knyvtrban. Nem fradtunk azonban feleslegesen, mert a szabvny knyvtri vltozat ugyangy funkcionl, mint a PELDA10.Cbeli. Hasznlathoz azonban be kell kapcsolni a prototpust tartalmaz STRING.H fejfjlt. rjuk be a PELDA10.C #include direktvja utn a
#include <string.h>

, s trljk ki a forrsszvegbl az strcmp prototpust s defincijt! Vgl prbljuk ki! Feltve, hogy s1 s s2 karaktertmbk, lssuk be, hogy C programban, ugyan szintaktikailag nem helytelen, semmi rtelme sincs az ilyen kdolsnak, hogy:
s1==s2, s1!=s2, s1>=s2, s1>s2, s1<=s2, s1<s2

E mdon ugyanis s1 s s2 memriabeli elhelyezkedst hasonltjuk ssze, azaz bizton llthatjuk, hogy az s1==s2 mindig hamis, s az s1!=s2 pedig mindig igaz.

38

BEVEZETS S ALAPISMERETEK

A getline ciklusvltozja ugyancsak zrusrl indul, s a ciklus vgig egyesvel halad. A ciklusmagbl ltszik, hogy a szabvny bemenetrl rkez karakterekkel tlti fel az s karaktertmbt. A ciklus akkor ll le, ha az s tmb kimerlt, s nem tlthet tovbb (i>=n), vagy ha a beolvasott karakter EOF jelzs (c==EOF), vagy ha a bejv karakter soremels (c==\n). Ezutn kitesszk a tmb iik elemre a lnczr zrust, s visszaadjuk it, azaz a behozott karakterlnc hosszt. getline fggvnynk j prblkozs az egy sor behozatalra a szabvny bemenetrl, de van nhny problmja, ha a bemenet a billentyzet: 1. A bemeneti pufferben lev karakterek csak soremels (Enter) utn llnak rendelkezsre, s addig nem. 2. Az EOF karakter (Ctrl+Z) rkezse nem jelenti tulajdonkppen a bemenet vgt, mert utna mg egy soremelst is vgre kell hajtani, hogy szlelni tudjuk. 3. Ha a bemeneti pufferben az Enter megnyomsakor nnl tbb karakter van, akkor a puffer (az egy sor) csak tbb getline hvssal olvashat ki. rjuk csak t a PELDA10.C mainjben a
while(getline(s,MAX)>0)

sort a kvetkezre
while(getline(s,4)>0)

, s a program indtsa utn gpeljk be a


JenJaniJojJani

sorokat! Az els kt problma az opercis rendszer viselkedse miatt van, s jelenleg sajnos nem tudunk rajta segteni. A msodik gondhoz mg azt is hozz kell fznnk, hogy fjlvget a billentyzet szabvny bemeneten a getlinenak csak res sor megadsval tudunk elidzni. A harmadik problma viszont knnyedn orvosolhat, csak ki kell olvasni a bemeneti puffert vgig a getlinebl val visszatrs eltt. Teht:
int getline(char s[],int n){ int c,i;

C programnyelv

39

for(i=0;i<n&&(c=getchar())!=EOF&&c!='\n';++i) s[i]=c; s[i]='\0'; while(c!=EOF&&c!='\n') c=getchar(); return(i); }

Megoldand feladatok: Ksztse el a kvetkez fggvnyeket, s prblja is ki ket egy rvid programmal! A void strcopy(char cl[], char forrs[]) tmsolja a cl karaktertmbbe a forrs karakterlncot a lezr nulljval egyetemben. A void strct(char s1[], char s2[]) egyesti s1be a kt paramter karakterlncot. Javasoljuk, hogy az algoritmus indexeljen elre s1 lnczr zrusig, s ettl kezdve csak ide kell msolni s2t! Az unsigned strlen(char s[]) visszaadja az s karakterlnc hosszt. Emlkezznk r, hogy a lnczr zrus indexe a karaktertmbben egyben a karakterlnc hossza is! A void strrv(char s[]) megfordtja a sajt helyn a paramter karakterlncot. Pldul a Janibl inaJnak kell szletnie. rjon programot, mely a getline segtsgvel sorokat olvas res sorig a szabvny bemenetrl, s megkeresi a legrvidebbet, s persze ki is jelzi a hosszval egytt! Javasoljuk, hogy a pillanatnyi minimum mentshez hasznlja fel az strcopy fggvnyt! 3.11 Loklis, globlis s bels, kls vltozk Ha alaposan ttanulmnyozzuk a PELDA10.Ct, akkor szrevehetjk, hogy mind az strcmpben, mind a getlineban van egyegy, int tpus i vltoz. Feltehetnnk azt a krdst, hogy mi kzk van egymshoz? A rvid vlasz nagyon egyszer: semmi. A hosszabb viszont kicsit bonyolultabb: Mindkt i vltoz loklis a sajt fggvnyblokkjra. A loklis vltoz hatskre az a blokk, amiben definiltk. A loklis vltoz lettartama az az id, mg sajt fggvnyblokkja aktv, azaz benne van a vezrls. Teht a kt i vltoznak a nvegyezsen tl nincs semmi kze sem egymshoz. A vltoz hatskre az a programterlet, ahol rvnyesen hivatkozni lehet r, el lehet rni. Nevezik ezt ezrt rvnyessgi tartomnynak is.

40

BEVEZETS S ALAPISMERETEK

A loklis vltoz hatskre az a blokk, s annak minden begyazott blokkja, amiben definiltk. A blokkon bellisge miatt a loklis vltozt bels vltoznak is nevezik. A vltoz lettartama az az idszak, amg memrit foglal. A loklis vltoz akkor jn ltre (rendszerint a veremben), amikor a vezrls bejut az t definil blokkba. Ha a vezrls kikerl onnt, akkor a memriafoglalsa megsznik, helye felszabadul (a veremmutat helyrejn). A fggvnyekben, gy a mainben is, definilt vltozk mind loklisak arra a fggvnyre, amelyben deklarltk ket. Csak az adott fggvnyen bell lehet hozzjuk frni. Ez a loklis hatskr. Futsi idben a fggvnybe val belpskor jnnek ltre, s kilpskor meg is semmislnek. Ez a loklis lettartam. A loklis vltoz kezdrtke ebbl kvetkezleg szemt. Pontosabban az a bitkombinci, ami azokban a memriabjtokban volt, amit most ltrejvetelekor a rendszer rendelkezsre bocstott. Teht a bels vltoznak nincs alaprtelmezett kezdrtke. Az alaprtelmezett kezdrtket implicit kezdrtknek is szoktk nevezni. Mi dnti el, hogy a bels vltoz melyik blokkra lesz loklis? Nyilvnvalan az, hogy az t definil deklarcis utastst melyik blokk elejn helyezik el. Tisztzzuk valamennyire a vltoz deklarcija s defincija kzti klnbsget! Mindkt deklarcis utastsban megadjk a vltoz nhny tulajdonsgt, de memriafoglalsra csak definci esetn kerl sor. A loklis vltoz az elzekben ismertetetteken kvl auto trolsi osztly is. Egsztsk ki jbl a deklarcis utasts szintaktikjt!
<trolsiosztly><tpusmdostk> <alaptpus> azonostlista;

A trolsiosztly a kvetkez kulcsszavak egyike lehet: auto, register, static, vagy extern. E fejezet keretben nem foglalkozunk a register s a static trolsi osztllyal! A szintaktikt betartva most felrhat, hogy
auto i;

, ami az auto signed int tpus, i azonostj vltoz defincija. Hogyan lehet auto trolsi osztly a loklis vltoz? Nyilvn gy, hogy a loklis helyzet deklarcis utastsban az auto trolsi osztly

C programnyelv

41

alaprtelmezs. Az auto kulcssz kirsa ezekben az utastsokban teljesen felesleges. Foglaljuk ssze a loklis vltozval, vagy ms nven bels vltozval, kapcsolatos ismereteinket! Neve: Tpusa: Hatskre: lettartama: Alaprtelmezett trolsi osztlya: Alaprtelmezett kezdrtke: Deklarcis utastsnak helye: Brmilyen azonost. Valamilyen tpus. Az a blokk, ahol definiltk. Amg a blokk aktv, azaz a vezrls benne van. auto Nincs. Annak a blokknak a deklarcis rsze, ahol a vltozt hasznlni kvnjuk. Futsi idben, tbbnyire a veremben.

Memriafoglals:

Meg kell emlteni, hogy a fggvnyek formlis paramterei is loklis vltozknak minslnek, de deklarcijuk a fggvnydefinci fej rszben s nem a blokkjban helyezkedik el, s rtkk az aktulis paramter rtke. Az elfeldolgozson tesett forrsmodult fordtsi egysgnek nevezik. A fordtsi egysg fggvnydefincikbl s kls deklarcikbl ll. Kls deklarci alatt a minden fggvny testn kvli deklarcit rtjk. Az gy definilt vltozt kls vltoznak, vagy globlis vltoznak nevezik. Az ilyen vltoz: Hatskre a fordtsi egysgben a deklarcis utastsnak pozcijtl az n. deklarcis ponttl indul, s a modul vgig tart. Ezt a hatskrt fjl hatskrnek, vagy globlis hatskrnek nevezik. lettartama a program teljes futsi ideje. A memria hozzrendels mr fordtsi idben megtrtnik. Rendszerint az elsdleges adatterleten helyezkedik el, s biztos, hogy nem a veremben. Ez a statikus lettartam.

42

BEVEZETS S ALAPISMERETEK

Alaprtelmezett (implicit) kezdrtke: minden bitje zrus. Ez az aritmetikai tpusoknl zrus. Karaktertmb esetben ez az res karakterlnc, hisz rgtn a lnczr nullval indul. Alaprtelmezett trolsi osztlya extern. Bnjunk csnjn az extern kulcssz explicit hasznlatval, mert deklarcis utastsban val megadsa ppen azt jelenti, hogy az utasts nem definci, hanem csak deklarci! Ms fordtsi egysgben definilt kls vltozt kell ebben a forrsmodulban gy deklarlni a r val hivatkozs eltt. /* Forrsmodul 1 */ extern int i; extern char t[]; /* . . . */ /* Forrsmodul 2 */ int i; char t[10]; /* . . . */
7. bra: extern deklarci

Prodzsektnk lljon e kt forrsmodulbl! Az int tpus i vltozt s a t karaktertmbt Forrsmodul 2ben definiltk. Itt trtnt teht meg a helyfoglalsuk. Ezek a vltozk Forrsmodul 1ben csak akkor rhetk el, ha a rjuk trtn hivatkozs eltt extern kulcsszval deklarljk ket. Vegyk szre, hogy a Forrsmodul 1 elejn elhelyezett deklarcis utastsok egyrszt globlis szinten vannak, msrszt valban csak deklarcik, hisz a tmb deklarcijban mret sincs! Persze azt, hogy a tmb mekkora, valahonnt a Forrsmodul 1ben is tudni kell! rjuk t gy a PELDA10.Ct, hogy a beolvasott sor trolsra hasznlatos tmbt globliss tesszk, majd nevezzk t PELDA11.Cre!
/* PELDA11.C: Nvszmlls */ #include <stdio.h> #define NEV "Jani" /* A szmllt nv. */ #define MAX 29 /* A bemeneti sor maximlis mrete. Most egyben a leghosszabb nv is. */ int getline(void); /* A fggvny prototpusok. */ int strcmp(char s2[]); void main(void){ int db; /* Nvszmll. */ db=0; /* A szmll nullzsa. */ printf("A(z) %s nv leszmllsa a bemeneten.\nAdjon\ meg soronknt egy nevet!\nProgramvg: res sor.\n",NEV); /* Sorok olvassa res sorig a bemenetrl: */

C programnyelv

43

while(getline()>0) /* Ha a sor pp a NEV: */ if(strcmp(NEV)==0) ++db; /* Az eredmny kzlse: */ printf("A nevek kzt %d darab %s volt.\n",db,NEV); } char s[MAX+1]; /* Az aktulis nv */ int strcmp(char s2[]){ int i; for(i=0; s[i]!=0&&s[i]==s2[i]; ++i); return(s[i]-s2[i]);} int getline(void){ int c,i; for(i=0;i<MAX&&(c=getchar())!=EOF&&c!='\n';++i) s[i]=c; s[i]='\0'; while(c!=EOF&&c!='\n') c=getchar(); return(i); }

Vegyk szre, hogy a PELDA10.Cs verzihoz kpest a getline paramter nlkliv vlt, s az strcmpnek meg egyetlen paramtere maradt! Miutn a globlis s karaktertmb deklarcis pontja megelzi a kt fggvny defincijt, a fggvnyblokkok s hatskrben vannak. Bellk teht a tmb egyszer hivatkozssal elrhet, s nem kell paramterknt tadni. A getline msodik paramtere tulajdonkppen a MAX szimbolikus konstans volt, s ezt beptettk a for ciklusba. Elemezgessk egy kicsit a kls vltoz, vagy paramter problmt! Globlis vltozkat hasznlva megtakarthat fggvnyhvskor a paramterek tadsa, vagyis kevesebb paramterrel oldhat meg a feladat. Kls vltozkat manipull fggvnyek msik programba viszont csak akkor msolhatk t s hvhatk meg vltoztats nlkl, ha ezeket a vltozkat is velk visszk. Ltszik, hogy az ilyen fggvnyek mobilitst, jrafelhasznlhatsgt cskkentik a jrulkos, globlis vltozk. A csak paramtereket s loklis vltozkat alkalmaz fggvnyek tmsols utn vltoztats nlkl hasznlhatk ms programokban. Legfeljebb az aktulis paramterek lesznek msok a hvsban. Vilgos, hogy nincs egyrtelmen s ltalnosan ajnlhat megolds a problmra. A feladat konkrt sajtossgai dntik el, hogy mikor milyen fggvnyeket kell rni, kelleneke kls vltozk stb. Summzzuk a globlis vltozval, vagy ms nven kls vltozval, kapcsolatos ismereteinket!

44 Neve: Tpusa: Hatskre: lettartama:

BEVEZETS S ALAPISMERETEK Brmilyen azonost. Valamilyen tpus. Globlis, vagy fjl. A deklarcis ponttl a forrsmodul vgig tart. Statikus. A program teljes futsi ideje alatt l. extern Minden bitje zrus. A forrsmodul minden fggvnydefincijn kvl. Fordtsi idben, vagy legalbb is az indt program kezddse eltt.

Alaprtelmezett trolsi osztlya: Alaprtelmezett kezdrtke: Deklarcis utastsnak helye: Memriafoglals:

3.12 Inicializls Az inicializls kezdrtk adst jelent a deklarciban, azaz az inicializtorok kezdrtkkel ltjk el az objektumokat (vltozkat, tmbket stb.). A statikus lettartam objektumok egyszer a program indulsakor inicializlhatk. Implicit (alaprtelmezett) kezdrtkk tiszta zrus, ami: Zrus az aritmetikai tpusoknl. res karakterlnc karaktertmb esetn. A loklis lettartam objektumok inicializlsa minden ltrejvetelkkor megvalsul, de nincs implicit kezdrtkk, azaz szemt van bennk. Mdostsuk jra a deklarcis utasts szintaktikjt vltozkra s tmbkre!
tpus azonost<=inicializtor>; tpus tmbazonost[<mret>]<={inicializtorlista}>;

, ahol az inicializtorlista inicializtorok egymstl vesszvel elvlasztott sorozata, s a tpus a <trolsiosztly><tpusmdostk> <alaptpus>t helyettesti. A vltozkat egyszeren egy kifejezssel inicializlhatjuk, mely kifejezs opcionlisan {}-be is tehet. Az objektum kezdeti rtke a kifejezs rtke lesz. Ugyanolyan korltozsok vannak a tpusra, s ugyanazok a

C programnyelv

45

konverzik valsulnak meg, mint a hozzrendels opertornl. Magyarn az inicializtor hozzrendels-kifejezs. Pldul:
char y = z, k; int a = 10000; /* y z rtk, s a knak meg */ /* nincs kezdrtke. */ /* a 10000 kezdrtk. */

C-ben a statikus lettartam vltozk inicializtora csak konstans kifejezs lehet, ill. csak ilyen kifejezsek lehetnek tmbk inicializtorlistjban. A loklis objektumok inicializtoraknt viszont brmilyen leglis kifejezs megengedett, mely hozzrendels kompatibilis rtkk rtkelhet ki a vltoz tpusra.
#define N 20 int n = N*2; /* Statikus objektum inicializtora csak konstans kifejezs lehet. */

/* . . . */ void fv(int par){ int i = N/par; /* A loklis objektum viszont brmilyen leglis kifejezs. . . . */ }

Emlkezznk vissza, hogy globlis vltozk csak egyszer kapnak kezdrtket: a program indulsakor. A loklis objektumok viszont mindannyiszor, valahnyszor blokkjuk aktvv vlik. A szvegben hasznlatos objektum fogalom nem objektum orientlt rtelm, hanem egy azonosthat memria terletet takar, mely konstans vagy vltoz rtk(ek)et tartalmaz. Minden objektumnak van azonostja (neve) s adattpusa. Az adattpus rgzti az objektumnak lefoglaland memria mennyisgt s a benne trolt informci belsbrzolsi formjt. Tmbk esetn az inicializtorlista elemeinek szma nem haladhatja meg az inicializland elemek szmt!
float tomb[3] = {0., 1., 2., 3.}; /* HIBS: tbb inicializtor van, mint tmbelem. */

Ha az inicializtorlista kevesebb elem, mint az inicializland objektumok szma, akkor a maradk objektumok a statikus lettartam implicit kezdrtk ads szablyai szerint kapnak rtket, azaz nullzdnak:
float tmb[3] = {0., 1.}; /* tmb[0]==0.0, tmb[1]==1.0 s tmb[2]==0.0. */

Az inicializland objektum lehet ismeretlen mret is, ha az inicializtorlistbl megllapthat a nagysg. Pldul:

46

BEVEZETS S ALAPISMERETEK
/* Az itmb hrom elem lesz. */ /* A lezr \0 karakter */ /* miatt 5 elemek a tmbk.*/

int itmb[] = { 1, 2, 3}; char nev[] = Lali, csaladnev[] = Kiss;

Megoldand feladatok: rja t az eddig elksztett PELDAn.Cket, s a megoldott feladatok programjait gy, hogy a vltozk kezdrtket mindig inicializlssal kapjanak!

C programnyelv

47

4 TPUSOK S KONSTANSOK
A fordt a forrskdot szintaktikai egysgekre, vagy ms elnevezssel szimblumokra, s fehr karakterekre trdeli. A tbb egymst kvet fehr karakterbl csak egyet tart meg. Ebbl kvetkezleg: Egyetlen C utasts akr szimblumonknt klnkln sorba rhat. Egy sorban azonban tbb C utasts is megadhat. Pontosan hat szimblum (int i ; float f ;) lesz a kvetkezkbl:
int i ; float

vagy
int i; float f;

A karakter, vagy karakterlnc konstansokban elfordul, akrhny fehr karaktert vltozatlanul hagyja azonban a fordt. Emltettk mr, hogy a programnyelv szimblumokbl (token) ll. Most ismertetjk a szimblum defincijt mdostott BackusNaur, metanyelvi lerssal:
szimblum (token): opertor kulcssz elvlaszt-jel azonost konstans karakterlnc (string literal)

Az rtelmezs nagyon egyszer: a felsorolt hat fogalom mindegyike szimblum. Az opertorokkal nem ebben a szakaszban foglalkozunk, hanem a MVELETEK S KIFEJEZSEKben! A kulcsszavak: auto break case char const continue double else enum extern float for int long register return short signed struct switch typedef union unsigned void

48 default do goto if sizeof static

TPUSOK S KONSTANSOK volatile while

Vannak ezeken kvl mg nem szabvnyos kulcsszavak s ms vdett azonostk, de ezek mindig megtudhatk a programfejleszt rendszer segtsgbl! Ilyenekre kell gondolni, mint a cdecl, a pascal, az stdcall, vagy egykt alhzs karakterrel kezddkre, mint pldul az __STDC__ stb. ANSI C kompatibilis fordtst elrva mindig kiderthet, hogy az illet fordt mely kulcsszavai, opertorai, vagy elvlasztjelei nem szabvnyosak. 4.1 Elvlaszt-jel A szintaktikai egysgeket (a szimblumokat) egymstl legalbb egy fehr karakterrel el kell vlasztani. Nincs szksg azonban az elvlaszt fehr karakterre, ha a kt nyelvi egysg kz a szintaktikai szablyok szerint egybknt is valamilyen elvlaszt-jelet kell rni. Az opertorok is elvlasztjelnek minslnek kifejezsekben.
elvlaszt-jel: (a kvetkezk egyike!) [](){}*,:=;#

Nzzk meg nhny elvlasztjel funkcijt! Az utastst zr pontosvessz minden pldban benne van. A kerek zrjeleknek csoportost funkcija van kifejezsekben. Van, amikor a szintaktika rsze. Fggvnyhvsnl az aktulis paramtereket, fggvnydeklarciban s definciban a formlis paramtereket ebbe kell tenni.
d = c*(a+b); if(d==z) ++x; fv(akt, par); void fv2(int n);

A szgletes zrjelek tmbk deklarcijban s indexel opertorknt hasznlatosak.


char kar, lanc[] = Sztan s Pan.; kar = lanc[3];

A kapcsos zrjelekbe tett tbb utasts szintaktikailag egyetlen utastsnak minsl. A dolgot sszetett utastsnak, blokknak nevezzk. Az sszetett utasts (blokk) zr kapcsos zrjele utn tilos pontosvesszt tenni!

C programnyelv

49

if(a<b){ /* Illeglis pontosvessz hasznlat. */ a=2; z=b+6; };

A csillag elvlaszt-jelnek tbbfle szerepe van a nyelvben. Eddig csak a szorzs opertor funkcijt ismerjk!
a = 3.14*b;

Az egyenlsg jel hozzrendels opertor, s deklarcis utastsban elvlasztja a vltozt az inicializtortl, vagy inicializtorlisttl.
char tomb[5] = {0, 1, 2, 3, 4}; int x=5, b, c=4; b = x+c;

A ketts kereszt elfeldolgoz (preprocessor) direktva kezdete. A sorbeli els nem fehr karakternek kell annak lennie.
#include <stdio.h> #define TRUE 1 #define FALSE 0

4.2

Azonost

azonost: nem-szmjegy azonost nem-szmjegy azonost szmjegy nem-szmjegy: (a kvetkezk egyike!) a ... z A ... Z _ szmjegy: (a kvetkezk egyike!) 0123456789

Az azonost vltozknak, fggvnyeknek, felhasznl definilta adattpusoknak stb. adott, a kvetkez pontokban pontostott nv: Kisbetvel, nagybetvel vagy alhzs (_) karakterrel kteles kezddni. A tovbbi karakterek lehetnek szmjegyek is. Az azonost nem lehet kulcssz vagy valamilyen elredefinilt, vdett azonost. Az azonostk kis- s nagybet rzkenyek, azaz az Osszeg, az osszeg vagy az osszeG hrom klnbz azonost. Kerlni kell a kt s az egy alhzs karakterrel kezdd nevek hasznlatt is!

50

TPUSOK S KONSTANSOK

Az azonostk els, mondjuk, 31 karaktere szignifikns. Ez azt jelenti, hogy hosszabb nevek is hasznlhatk, de az els 31 karakterkben nem klnbz azonostk ugyanannak minslnek. Az, hogy az azonost els hny karaktere szignifikns, a fordttl fgg. Msrszt a programfejleszt rendszerben egy s ezen rtk kztt vltoztathat is! Bizonyos kls kapcsolds azonostkra, melyeket a kapcsol szerkeszt illeszt, eltr megszortsok lehetnek rvnyben. Pldul kevesebb karakterbl llhatnak, vagy nem kis s nagybet rzkenyek stb. Megoldand feladat: A felsorolt pldaazonostk kzl az els hrom hibs. Magyarzza meg, hogy mi a problma velk! 6os_villamos Moszer Aranka Nagy_Jnos Nagy_Jani puffer 4.3 Tpusok s konstansok a nyelvben A nyelvben sszesen ngy tpuskategria van: A fggvny a nyelv kdgenerl egysge. Az sszes tbbi kategria csak memrit foglal az adatoknak. A void tpus tbbnyire valaminek a meg nem ltt jelzi. Pldul nincs paramtere s visszaadott rtke a void main(void) fggvnynek. Skalr az aritmetikai tpus, mely tovbb bonthat fixpontos egsz s lebegpontos vals brzols tpusokra. Ilyen a felsorols (enum) tpus s a mutat is. Aggregtum a tmb, a struktra s az uni. A mutatkkal, a struktrkkal s az unikkal ksbbi szakaszokban foglalkozunk! A tpusokat gy is csoportosthatnnk, hogy vannak alaptpusok s

C programnyelv szrmaztatott tpusok

51

Az alaptpusok a void, a char, az int, a float s a double. A szrmaztats pedig a short, a long, a signed s az unsigned n. tpusmdostkkal trtnhet. A short s a long, valamint a signed s az unsigned egymst kizr mdost prok, de a kt pr egyazon alaptpusra egyszerre is alkalmazhat, azaz ltezik unsigned long int vagy signed short int stb. A signed s az unsigned mdost azonban csak egsz tpusokra (char s int) alkalmazhat, lebegpontos vals s a void alaptpusra nem. A szrmaztatott tpusokba mindig belertendk az ilyen tpus rtkkel visszatr fggvnyek, az ilyen tpust paramterknt fogad fggvnyek, a tmbk stb. Ha programunkban valamilyen azonostt hasznlni kvnunk, akkor elbb deklarlni (definilni) kell. A deklarci teremti meg a kapcsolatot az azonost s az objektum kztt, s rgzti legalbb az objektum adattpust. A szrmaztatott tpust is szoks egyszeren tpusnak nevezni. Kszpnzre vltva az elz bekezdsben mondottakat: ha a tpus valamilyen nem void adattpus, akkor a deklarcik kvetkezkpp szemlltethetk:
/* Hrom tpus tpus objektum. */ /* Tpus tpus rtket visszaad, paramter nlkli fggvny. */ void fv(tpus i); /*Tpus tpus paramtert fogad eljrs. */ tpus tt[10]; /* 10 elem, tpus tpus tmb. Az elemek rendre: tt[0], ..., tt[9]. */ tpus t, t1, t2; tpus f(void);

Felttlenl emltst kell tennnk mg kt, definciban hasznlhat mdostrl, melyek alaposan megvltoztatjk a deklarlt objektum tulajdonsgait. A const nem mdosthat objektumot definil, azaz meggtolja a hozzrendelst az objektumhoz, s az olyan mellkhatsokat, mint az inkrementls, dekrementls stb. Magyarn: nem engedi meg az azonost elrst balrtkknt. A const a deklarci elejn brmilyen alaptpussal, aggregtum tpussal stb. llhat, de tilos tbbtteles deklarci els vesszje utn kirni:
float f = 4.5, const cf = 5.6; /* HIBS. */

52

TPUSOK S KONSTANSOK

Ha a const objektum nem lehet balrtk, akkor hogyan lehet valamilyen rtkkel elltni? A const tpus objektum rtkkel val elltsnak egyetlen mdja az inicializls. Teht az ilyen objektum defincijban ktelez neki kezdrtket adni, mert ez ksbb mr nem tehet meg. Pldul:
const float pi = 3.1415926; const max2int = 32767;

Az el nem rt alaptpus miatt a deklarci specifiktorknt egyedl ll const tulajdonkppen const int, s ezrt a max2int is az. E deklarcik utn botor dolgok a kvetkez utastsok:
pi = 3.; int i = max2int++; /* Szintaktikai hiba. */ /* Szintaktikai hiba. */

A volatile sz jelentse elprolg, illkony, llhatatlan. Mdostknt azt jelzi, hogy az illet vltoz rtke program vgrehajtson kvli okbl is megvltozhat. Mi lehet a program vgrehajtsn kvli ok? Pldul megszakts, B/K port, konkurensen fut vgrehajtsi szl stb. A volatile kulcssz deklarcin belli elhelyezsnek szablyai egyeznek a constival. A fordt az ilyen vltozt nem helyezheti el regiszterben, ill. az ilyen objektumot is tartalmaz kifejezs kirtkelse sorn nem indulhat ki abbl, hogy az rtk kzben nem vltozik meg. Szval az ilyen vltoz minden hivatkozshoz elrsi kdot kell generlnia akkor is, ha az ltszlag hatstalannak tnik. Pldul:
volatile ticks; /* volatile int a tpus. */ void interrupt timer(void) {++ticks;} void varj(int ennyit){ ticks =0; while( ticks < ennyit ); } /* Ne tegyen semmit. */

A while-beli felttel kirtkelsekor a ciklus minden temben tlteni kell ticks rtkt. Lttuk, hogy egy objektumot egsz lettartamra volatilel tehetnk deklarcival, de explicit tpusmdost szerkezettel a vltoz egyetlen hivatkozst is volatilel minsthetjk.
int ticks; void interrupt timer(void) {++ticks;} void varj(int ennyit){ ticks =0; while( (volatile)ticks < ennyit ); }

C programnyelv

53

Egy objektum egyszerre lehet const s volatile, amikor is az t birtokl program nem mdosthatja, de megvltoztathatja az rtkt brmely aszinkron program vagy vgrehajtsi szl. Az adattpusok s konstansok trgyalsnak megkezdse eltt lssuk a konstans defincijt!
konstans: egsz-konstans enum-konstans lebegpontos-konstans karakter-konstans

Tudjuk, hogy az objektum is lehet konstans, de itt most nem ilyen llandrl van sz. Az igazi konstans nem azonosthat memria terletet takar, mely fix, a program futsa alatt meg nem vltoztathat rtket tartalmaz. A konstansnak nincs szoftveresen is hasznlhat cme, de van adattpusa, mely meghatrozza az llandnak lefoglaland memria mennyisgt s a benne trolt rtk belsbrzolsi formjt. 4.3.1 Egsz tpusok s konstansok Mr emltettk, hogy az egsz tpusok a char s az int alaptpusbl a signed - unsigned, valamint a short - long mdost prok alkalmazsval llthatk el, azaz a deklarci rsszablya eltekintve a trolsi osztlytl s az inicializlstl:
<tpusmdostk> <alaptpus> azonostlista;

A tpusmdostk alaptpus prost azonban a tovbbiakban is tpusnak fogjuk nevezni. A szablyok a kvetkezk: Mind az alaptpus, mind a tpusmdostk elhagyhat. A kett egytt azonban nem. Ha elhagyjuk az alaptpust, alaprtelmezs az int. A short - long mdostk elhagysakor nincs rjuk vonatkoz alaprtelmezs. Ha elhagyjuk a signed - unsigned mdostt, alaprtelmezs a signed.

54 Tpus char, signed char unsigned char short, short int, signed short int unsigned short, unsigned short int int, signed int unsigned, unsigned int long, long int, signed long int unsigned long, unsigned long int

TPUSOK S KONSTANSOK Mret Minimlis rtk Maximlis rtk bjtban 1 1 2 2 -128 0 -32768 0 127 255 +32767 65535 short vagy long ugyangy, de unsigned +2147483647 4294967295

2 vagy 4 short vagy long 2 vagy 4 4 4 ugyangy, de unsigned -2147483648 0

8. bra: Egsz tpusok

A char alaptpussal kapcsolatban kiegsztsre szorul a signed char alaprtelmezs! A programfejleszt rendszerben ugyanis unsigned char is bellthat a char tpus alaprtelmezseknt. A lehetsges karaktertpusok ilyenkor gy vltoznak: Tpus char, unsigned char signed char Mret Minimlis rtk Maximlis rtk bjtban 1 1 0 128 255 127

A tblzatbl (8. bra) is jl ltszik, hogy az ANSI szabvny nem r el pontos mretet az int tpusra, csupn csak annyit, hogy: short <= int <= long. A belsbrzols fixpontos, binris egsz, ezrt signed tpusokra a negatv szmokat kettes komplemensk alakjban trolja a fordt.

C programnyelv

55

A szabvnyos LIMITS.H fejfjlban tallunk szimbolikus llandkat (CHAR_MIN, UCHAR_MAX, SHRT_MIN, stb.) az brzolsi korltokra! A nyelv szerint nincs sem egsz tlcsorduls, sem alulcsorduls. Az egsz konstansnl lthatunk erre is egy rvid pldt!
egsz-konstans: decimlis-konstans <egsz-uttag> oktlis-konstans <egsz-uttag> hexadecimlis-konstans <egsz-uttag>

A metanyelvben a < ... > az elhagyhatsgot jelli!


egsz-konstans: decimlis-konstans<egsz-uttag> oktlis-konstans<egsz-uttag> hexadecimlis-konstans<egsz-uttag> decimlis-konstans: nemzrus-szmjegy decimlis-konstans szmjegy oktlis-konstans: 0 oktlis-konstans oktlis-szmjegy hexadecimlis-konstans: 0xhexadecimlis-szmjegy 0Xhexadecimlis-szmjegy hexadecimlis-konstans hexadecimlis-szmjegy nemzrus-szmjegy: (a kvetkezk egyike!) 123456789 oktlis-szmjegy: (a kvetkezk egyike!) 01234567 hexadecimlis-szmjegy: (a kvetkezk egyike!) 0123456789abcdefABCDEF egsz-uttag: unsigned-uttag<long-uttag> long-uttag<unsigned-uttag> unsigned-uttag: (a kvetkezk egyike!) uU long-uttag: (a kvetkezk egyike!) lL

A definci szerint: Az egsz konstans lehet decimlis, oktlis s hexadecimlis. Az egsz konstansnak nincs eljele (pozitv), azaz a negatv egsz konstans eltt egy egyoperandusos mnusz opertor ll.

56

TPUSOK S KONSTANSOK

A decimlis egsz konstans int, long int, vagy unsigned long int belsbrzols a konstans rtktl fggen alaprtelmezs szerint. Az oktlis s hexadecimlis egsz konstans az rtktl fggen int, unsigned int, long int, vagy unsigned long int belsbrzols. Decimlis 032767 Oktlis 0077777 0100000 01777777 32767 02000000 2147483647 017777777777 Hexadecimlis 0X00X7FFF 0X8000 0XFFFF 0X10000 0X7FFFFFFF int unsigned long unsigned long Tpus

2147483648 020000000000 0X80000000 4294967295 037777777777 0XFFFFFFFF

9. bra: Egsz konstansok belsbrzolsa

Lssunk nhny pldt a decimlis, az oktlis s a hexadecimlis egsz konstansokra!


int int int int i j k l = = = = 10; 010; 0; 0XFF; /* j kezdetben decimlisan 8. */ /* k decimlisan s oktlisan is 0. */ /* k decimlisan 255. */

Explicit egsz uttagot a konstans utna rva megvltoztathatjuk az alaprtelmezett belsbrzolst. Az inicializtor konstansok tpusegyeztetsvel elkerlhet a szksgtelen kzbens konverzi. Pldul:
unsigned ui=2u; long li=16l; unsigned long uli=17lu;

Az egsz konstans 0 s 4294967295 rtkhatrok kzt megengedett. Ez azonban a programoz felelssge, mert a 4294967295-nl nagyobb rtk egyszeren csonkul. Legfeljebb egy halk figyelmeztet zenet jhet a fordttl. Pldul a
#include <stdio.h> void main(void){ unsigned long pipipp; pipipp = 4300000000; printf(Pipipp = %ld\n, pipipp); }

hatsra 5032704 jelenik meg, ami egy hjn 4300000000 - 4294967295.

C programnyelv

57

4.3.2 Felsorols (enum) tpus s konstans Az enum (enumerate) adattpus mnemonikuss tesz egy sorozat egsz rtkre val hivatkozst. Pldul az
enum napok{ vasar, hetfo, kedd, szerda, csut, pentek, szomb} nap;

deklarci ltrehoz egy egyedi enum napok egsz tpust. Helyet foglal egy ilyen tpus, nap azonostj vltoznak, s definil ht, konstans egsz rtket reprezentl enumertort: az enumertor kszletet. Felsorols tpus vltozban a tpushoz definilt enumertor kszlet egyik rtkt tarthatjuk. Az enumertort enum konstansnak is nevezik. Felsorols tpus vltozkat hasznlhatunk index kifejezsben, minden aritmetikai s relcis opertor operandusaknt, stb. Tulajdonkppen az enum a #define direktva alternatvjnak is tekinthet. ANSI Cben az enumertor rtkt definil kifejezs csak egszrtk konstans kifejezs lehet, s tpusa mindig int. Az enum vltoz trolshoz hasznlt memria is annyi, mint az int tpushoz hasznlt. enum tpus konstans vagy rtk a Cben ott hasznlhat, ahol egsz kifejezs is. Lssuk a szintaktikt!
enum-specifiktor: enum <azonost> {enumertorlista} enum azonost enumertorlista: enumertor enumertorlista, enumertor enumertor: enum-konstans enum-konstans = konstans-kifejezs enum-konstans: azonost

Az enum-specifiktor defincijban az enumertorlistval definilt enum tpus opcionlis azonostjt enum cmknek (tag) nevezzk. Ha ezt elhagyjuk a defincibl, nvtelen enum tpust kapunk. Ennek az az diuma, hogy ksbb nincs lehetsgnk ilyen felsorols tpus vltozk defincijra, hisz nem tudunk a nvtelen enumra hivatkozni. Nvtelen enum tpus vltozk teht csak a nvtelen enum deklarcijban definilhatk, mshol nem. Az
enum {jan, feb, marc, apr, maj, jun, jul, aug, szep, okt, nov, dec};

gy teljesen hasznlhatatlan, hisz kptelensg ilyen tpus vltozt deklarlni. A problma az azonostlista megadsval elkerlhet:

58

TPUSOK S KONSTANSOK

enum {jan, feb, marc, apr, maj, jun, jul, aug, szep, okt, nov, dec} ho=jan, honap;

Foglaljuk ssze az enum deklarcival kapcsolatos tapasztalatainkat:


enum <enum cmke> <{enumertorlista}> <azonostlista>;

, ahol a < > most is az elhagyhatsgot jelli. Az enum cmke megadsa teht azt biztostja, hogy az "enum enum cmke" utn azonostlistt rva ksbb ilyen felsorols tpus vltozkat definilhatunk. A fejezet elejn emltett deklarcis pldban a napok azonost az opcionlisan megadhat enum cmke, mely ksbb enum napok tpus vltozk definciiban hasznlhat fel. Pldul:
enum napok fizetes_nap, unnepnap;

Trjnk egy kicsit vissza az enumertorokhoz! Lttuk, hogy az enum konstans a felsorols tpus deklarcijban definilt azonost. Miutn az enumertor egsz adattpus, kifejezsekben ott hasznlhat, ahol egybknt egsz konstans is megadhat. Az enumertor azonostjnak egyedinek kell lennie az enum deklarci hatskrben belertve a norml vltozk neveit s ms enumertorlistk azonostit. Az enum konstansok az egyszer vltozk nvterletn helyezkednek el teht. Az enum cmkknek viszont az enum, a struktra s az unicmkk nvterletn kell egyedinek lennik. A nvterlet olyan azonost csoport, melyen bell az azonostknak egyedieknek kell lennik. A Cben tbb nvterlet van, amibl itt kettt meg is emltettnk. Kt klnbz nvterleten ltez, ugyanazon azonostnak semmi kze nincs egymshoz. A nvterleteket majd egy ksbbi fejezetben trgyaljuk! Lssunk pldkat ezen kzbevets utn!
int hetfo = 11; /* A hetfo egyszer int tpus vltoz.*/ { enum napok{ vasar, hetfo, kedd, szerda, csut, pentek, szomb}; /* A hetfo enumertor ebben a blokkban elrejti a hetfo int tpus vltozt. */ double csut; /* HIBS, mert ezen a nvterleten van mr egy csut enumertor. /* . . . */ } hetfo+=2; /* OK, mert itt mr csak a hetfo int vltoz ltezik. */

Az enum napok deklarciban szerepl enum konstansok implicit mdon kaptak rtket zrustl indulva s mindig eggyel nvekedve. A vasar gy 0, a hetfo 1, ..., s a pentek 6. Az enum konstansok azonban explici-

C programnyelv

59

ten is inicializlhatk. Akr negatvak is lehetnek, ill. tbb enumertor lehet azonos rtk is. Pldul:
enum ermek{ egyes=1, kettes, otos=5, tizes=2*otos, huszas=kettes*tizes, szazas=otos*huszas};

Vegyk szre, hogy az enumertor definilt mr az enumertorlista kvetkez tagjhoz rve! Mondottuk, hogy a felsorols tpusok mindentt feltnhetnek, ahol egybknt az egsz tpusok megengedettek. Pldul:
enum napok{ vasar, hetfo, kedd, szerda, csut, pentek, szomb}; enum napok fizetes_nap = szerda, nap; int i = kedd; /* OK */ nap = hetfo; /* OK */ hetfo = kedd; /* HIBS, mert a hetfo konstans. */

enum tpus vltozhoz egsz rtk hozzrendelse megengedett, de explicit tpusmdost szerkezet hasznlata javallott. Teht a
fizetes_nap = 5;

helyett
fizetes_nap = (enum napok) 5;

rand. A fordtban nincs mechanizmus a konvertland egsz rtk rvnyessgnek ellenrzsre! Pontosabban a kvetkez lehetetlensg elkerlse a programoz felelssge:
n = (napok) 958;

Az enum tpus egsz (integral) tpus. Implicit mdon konvertl gy egssz brmely enumertort a fordt, de a msik irnyban az explicit tpusmdosts javasolt:
int i; enum napok n = szomb; i = n; n = 5; n = (napok) 5; /* /* /* /* OK */ OK */ Nem javasolt! */ OK */

Egsz tpusnak az egsz rtkek trolsra alkalmas, aritmetikai adattpusokat (char, short, int, long s enum) nevezzk.

60

TPUSOK S KONSTANSOK

4.3.3 Vals tpusok s konstans Az IEEE lebegpontos belsbrzols vals alaptpusok a float s a double. Egyedl a long mdost hasznlata megengedett, s az is csak a double eltt, azaz van mg long double szrmaztatott tpus. A float tpus, egyszeres pontossg lebegpontos brzols 4 bjtot foglal, melybl 8 bit a 128 tbbletes, binris exponens, 1 bit a mantissza eljele (1 a negatv!) s 23 bit a mantissza. A mantissza 1.0 s 2.0 kztti szm. Miutn a mantissza legnagyobb helyirtk bitje mindig egy, az brzols ezt nem trolja. eljel eltrtett karakterisztika mantissza 22 - 0 bitek mantissza 51 - 0 bitek 31. bit 30 23 bitek eljel eltrtett karakterisztika

A double exponens 11-, s a mantissza 52 bites. 63. bit 62 52 bitek

A lebegpontos belsbrzols mretei, megkzelt hatrai s decimlis jegyben mrt pontossga a kvetkez tblzatban lthat: Tpus float double long double Mret bjtban 4 8 10 Hatrok: 3.4*10-38 - 3.4*10+38 Pontossg: 67 decimlis jegy

1.7*10-308 - 1.7*10+308 1516 decimlis jegy 1.2*10-4932 - 1.2*10+4932 19 decimlis jegy


10. bra: Lebegpontos tpusok

Ehhez mr csak annyit kell hozzfzni, hogy lebegpontos zrus az, amikor a belsbrzols minden bitje zrus. A lers az IEEE szabvnyos lebegpontos brzolsrl szl, de elkpzelhet, hogy a konkrt fordt ms formval dolgozik. A szabvnyos FLOAT.H fejfjlban azonban mindig megtallhatk szimbolikus llandk (FLT_MIN, DBL_MAX stb.) alakjban az brzolsi hatrok s ms konkrtumok az aktulis belsbrzolsrl.
lebegpontos-konstans: trt-konstans <exponens-rsz> <float-uttag> szmjegy-sor exponens-rsz <float-uttag>

C programnyelv
trt-konstans: <szmjegy-sor> . szmjegy-sor szmjegy-sor . exponens-rsz: e <eljel> szmjegy-sor E <eljel> szmjegy-sor eljel: (a kvetkezk egyike!) +szmjegy-sor: szmjegy szmjegy-sor szmjegy float-uttag: (a kvetkezk egyike!) flFL

61

A mantisszbl elhagyhat a decimlis egsz rsz vagy a trt rsz, de a kett egytt nem. Elhagyhat a tizedes pont vagy az exponens rsz, de mindkett nem. A lebegpontos konstans eljeltelen (pozitv). A negatv lebegpontos konstans eltt egy egyoperandusos mnusz opertor ll. Float uttag nlkl a lebegpontos konstans double belsbrzols. Az uttag megadsval kiknyszerthetjk a float (f vagy F), ill. a long double (l vagy L) belsbrzolst. double belsbrzols lebegpontos konstansok:
-.5e35, 5., 5E-4, 3.4

Ugyanezek float s long double belsbrzolsban:


-.5e35f, 5.L, 5E-4F, 3.4l

Az egyszeri programoz berta a forrsszvegbe a


float v=143736120; /* . . . */ printf(%9.0f\n, v);

sorokat, s meglepdtt a 143736128as eredmnyen. Ha v rtkt eggyel cskkentette, akkor meg 143736112t kapott. Mi lehet a problma? A 143736120 (0X8913D38) binrisan
1000 1001 0001 0011 1101 0011 1000

, de csak 24 bit fr el a belsbrzols mantisszjban. A fordt a legmagasabb helyirtk, lecsordul bit rtkvel megnveli a mantisszt. Most, a karakterisztikval nem foglalkozva, az j rtk:
1000 1001 0001 0011 1101 0100 0000

62

TPUSOK S KONSTANSOK

, ami ppen 143736128 (0X8913D40). A 143736119 (0X8913D37) binrisan


1000 1001 0001 0011 1101 0011 0111

esetben az eredmny
1000 1001 0001 0011 1101 0011 0000

lesz, ami143736112 (0X8913D30). Komolyan kell teht venni a lebegpontos brzolsok decimlis jegyben mrt pontossgt, s az brzolsi hatrokat. Mg kt dologra felttlenl oda kell figyelni: A decimlis vges tizedes trt tbbnyire nem vges binris trt, azaz az talakts odavissza nem teljesen pontos. Matematikai itercit, ami addig tart, mg kt, szmolt, vals rtk klnbsge zrus nem lesz, nem szabad egy az egyben megvalstani, mert az esetek tbbsgben vgtelen ciklushoz vezet. 4.3.4 Karakter tpus s konstans A karakter tpusrl mr sz esett az egsz tpusok trgyalsnl. Tudjuk, hogy hrom karakter tpus van: char, signed char s unsigned char. A karakter tpus objektum helyfoglalsa 1 bjt mindenkpp. A karakter konstans tpusra s helyfoglalsra rgtn kitrnk defincija utn!
karakter-konstans: 'c-karakter-sor' c-karakter-sor: c-karakter c-karakter-sor c-karakter c-karakter: brmilyen karakter aposztrf ('), fordtott per jel (\) s soremels (\n) kvtelvel escape-szekvencia

C programnyelv Szekvencia rtk \0 \a \b \t \n \v \f \r \ \ \? \\ \ooo \xhh \Xhh 0X00 0X07 0X08 0X09 0X0A 0X0B 0X0C 0X0D 0X22 0X27 0X3F 0X5C brmi brmi brmi Karakter NUL BEL BS HT LF VT FF CR ? \ brmi brmi brmi Funkci karakterlnc vge ftty visszatrls vzszintes tab soremels fggleges tab lapdobs kocsi vissza macskakrm aposztrf krdjel fordtott per jel max. 3 oktlis szmjegy max. 3 hexadec. jegy max. 3 hexadec. jegy

63

11. bra: Escape szekvencik

A defincibl kvetkezleg a karakter konstans aposztrfok kzt ll egy vagy tbb karakter, ill. escape szekvencia. Ezen az elven beszlhetnk egykarakteres s tbbkarakteres karakter konstansrl. A karakter konstans adattpusa mindenkppen int. Tudjuk, hogy az int helyfoglalsa 2 vagy 4 bjt, gy maximum ngy karakteres karakter konstans ltezhet. Pldul:
An, \n\r, Alfi, tag, \007\007\007

Tudjuk, hogy az int belsbrzols karakter konstans esetben is rvnyben van a signed char alaprtelmezs. Ennek kvetkeztben az int mretnl kevesebb karakterbl ll karakter konstansok eljel kiterjesztssel alakulnak int belsbrzolsv. Konkrt pldaknt tekintsk a 2 bjtos int-et, s a 852es kdlapot! Az bet kdja 130 (0X82) s az a bet 97 (0X61). Az -bl gy 0XFF82 s az a-bl 0X0061 lesz a memriban, s gy igaz lesz a kvetkez relci:

64
< a

TPUSOK S KONSTANSOK

Ha unsigned char az alaprtelmezs, akkor a fels bjt(ok)at bizonyosan 0X00-val tlti fel a fordt (az -bl 0X0082 lesz), s nem jn el az elbb taglalt problma. A kvetkez kis plda szemllteti a trgyalt karakter tpus s karakter konstans mreteket!
#include <stdio.h> #include <stdlib.h> #define CH 'x' /* Egykarakteres karakter konstans. */ void main(void) { char ch = CH; /* Karakter tpus vltoz. */ printf("Az int mrete :\t\t\t%d\n", sizeof(int)); printf("A char mrete :\t\t\t%d\n", sizeof(char)); printf("A karakter konstans mrete:\t %d\n", sizeof(CH)); printf("A karakter vltoz mrete:\t%d\n", sizeof(ch)); }

Szlnunk kell mg nhny szt a pontosts kedvrt az escape szekvencirl! A \ jelet kvetheti egy a tblzatban (11. bra) ismertetett karakter, vagy egy legfeljebb hromjegy oktlis szm, vagy x utn hexadecimlis szmjegyek. Pldul a \03 a Ctrl+C, a \x3F a ? karakter, stb. Szval az escape szekvencia lerhat vezrl s igazi karaktereket egyarnt. Nzzk mg a szablyokat! Ha az escape szekvencia fordtott per jelt nem leglis karakter kveti, akkor legfeljebb figyelmeztet zenetet kapunk, s a fordt elhagyja a \ jelet, de megtartja az illeglis karaktert. Pldul a \zbl a z marad meg. Az oktlisan vagy hexadecimlisan adott escape szekvencia vgt az els nem oktlis, ill. hexadecimlis karakter is jelzi. Pldul a \518-bl kt karakteres karakter konstans lesz, ahol a karakterek rendre a \51 s a 8, azaz )8. Vigyzzunk a nem egyrtelm megadsra is! Pldul a \712 kmves. karakterlncbl ltszik a programoz trekvse, azaz hogy a BEL karakter utn a 12 kmves szveg kvetkezne. Ez azonban gy szintaktikai hiba. A helyes eredmnyhez pldul \7 12 kmves. mdon juthatunk. Miutn az escape szekvencival tulajdonkppen egyetlen karaktert adunk meg, a \ jelet kvet oktlis szmrtk nem haladhatja meg a 0377et, a hexadecimlis a 0XFFet, vagyis a 255t!

C programnyelv

65

Igazbl a karakter a vgrehajtskor rvnyes gpi karakterkszletbl vett rtk. A knyvben vgig bjtos (char) kdkszletet (ASCII) tteleznk fel, s nem foglalkozunk a szles karaktertpussal (wchar_t) s az Unicode kdkszlettel! Az eddig trgyalt adattpusokat sszefoglal nvvel aritmetikai adattpusoknak is nevezhetjk. Az aritmetikai adattpusok zavarba ejt bsge a nyelvben azonban nem arra szolgl, hogy a programoz csak gy ukk-mukk-fukk kivlasszon valamilyen adattpust adatai s (rsz)eredmnyei trolsra, hanem arra, hogy a szmbrzolsi korltokat tekintetbe vve alaposan vgiggondolja, hogy rtk s pontossg veszts nlkl milyen adattpust kell hasznlnia az egyes adataihoz, (rsz)eredmnyeihez. Esetleg milyen explicit konverzikat kell elrnia a rszletszmtsok vgzse sorn a pontossg veszts elkerlshez. Ksztsen szorzat piramist! A piramis als sora 16 darab 100tl 115ig terjed egsz szm! A kvetkez sorokat gy kapjuk, hogy az alattuk lev sor elemeit pronknt sszeszorozzuk. A msodik sor gy nz ki teht: 100*101 = 10100, 102*103 = 10506, , 114*115 = 13110. A harmadik sorban a kvetkezk llnak: 10100*10506 = 106110600, 10920*11342 = 126854640, , 12656*13110 = 165920160, s gy tovbb. Figyeljnk arra, hogy az eredmnyl kapott szmokat mindig a lehet legkisebb helyen troljuk, de az rtkeknek nem szabad csonkulniuk!
/* PELDA12.C: Szorzat piramis */ #include <stdio.h> #define N 16 void main(void){ int i; /* Az als sor: 100-s nagysgrend. */ char n[N]={100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115}; short n1[N/2]; /* 2. sor: 10000-s nagysgrend. */ long n2[N/4]; /* 3. sor: 100000000 kb. */ long double n3[N/8]; /* 4. sor: 10 a 16-n kb. */ printf("Szorzat piramis: els szint\n"); for(i=0; i<N; i=i+2){ n1[i/2]=(short)n[i]*(short)n[i+1]; printf("%3d*%3d = %5hd\n", n[i], n[i+1], n1[i/2]); } printf("Szorzat piramis: msodik szint\n"); for(i=0; i<N/2; i=i+2){ n2[i/2]=(long)n1[i]*(long)n1[i+1]; printf("%5hd*%5hd = %9ld\n", n1[i], n1[i+1], n2[i/2]); } printf("Szorzat piramis: harmadik szint\n");

66

TPUSOK S KONSTANSOK

for(i=0; i<N/4; i=i+2){ n3[i/2]=(long double)n2[i]*(long double)n2[i+1]; printf("%9ld*%9ld = %17.0Lf\n", n2[i], n2[i+1], n3[i/2]); } printf("Szorzat piramis: cscs\n"); printf("%17.0Lf*%17.0Lf ~ %33.0Lf\n", n3[0], n3[1], n3[0]*n3[1]);}

Mr a PELDA7.C pldban tallkoztunk az egszekre vonatkoz h s l hosszmdostkkal a formtumspecifikcikban. Most a long double tpus paramter jelzsre val L hosszmdostt ismerhettk meg. Ha a szorzat piramis msodik szintjnek kiszmtst vgz
n2[i/2]=(long)n1[i]*(long)n1[i+1];

kifejezsbl elhagyjuk a kt, explicit (long) tpusmdostt, akkor a listarsz a kvetkezkpp mdosul:
Szorzat piramis: msodik szint 10100*10506 = 7816 10920*11342 = -8400 . . .

Ez vitathatatlanul rossz, de mirt? Az n1 tmb short tpus. A ktoperandusos szorzsi mvelet operandusai azonos tpusak, teht semmifle implicit konverzi nem trtnik, s az eredmny tpusa is short. A 32 bites regiszterben a 10100*10506os szorzs vgrehajtsa utn ugyan ott van a helyes eredmny, a 106110600 (0X6531E88)
0000 0110 0101 0011 0001 1110 1000 1000

, de a 16 bites short tpus miatt csak az als kt bjt kpezi az eredmnyt. Az meg 0X1E88, vagyis 7816. Megemltjk mg, hogy a cscs rtknek (n3[0]*n3[1]) pontossg veszts nlkli trolshoz mg a long double tpus is kevs! Megoldand feladatok: Ksztsnk programot, mely megllaptja az :PP:MM alak karakterlncrl, hogy rvnyes ide! Az ra zrus s 23, a perc, valamint a msodperc 0 s 59 kztti rtk lehet csak. Jelezze ki a szoftver, ha a karakterlnc formailag hibs, s krjen msikat! Ha rvnyes az id, akkor hatrozza meg s jelezze ki rtkt msodpercben!

C programnyelv

67

Egy msik szoftver fogadjon a szabvny bemenetrl egsz szmokat mindaddig, mg res sort nem adnak meg! Kpezze rendre az egsz szmok s kt szomszdjuk ngyzetsszegt! A szm szomszdja a nlnl eggyel kisebb s eggyel nagyobb rtk. A kpernyn jelentse meg fejlccl elltva, tblzatos formban a szmhrmasokat (a bevitt rtkek lljanak kzpen) s a ngyzetsszegeket! Legvgl adja meg a ngyzetszszegek sszegt is. A szmok legyenek jobbra igaztottak! 4.4 Karakterlnc (string literal): A karakterlncokrl mr sz volt a BEVEZETS S ALAPISMERETEK szakaszban! A definci:
karakterlnc: "<s-karakter-sor>" s-karakter-sor: s-karakter s-karakter-sor s-karakter s-karakter: brmilyen karakter idzjel ("), fordtott per jel (\) s a soremels (\n) kivtelvel escape-szekvencia (11. bra)

A karakterlnc adattpusa char tmb. A tmb mrete mindig eggyel hosszabb, mint ahny karakterbl a karakterlnc ll, mert a nyelv a lnc vgt \0 karakterrel jelzi. A karakterlnc konstansokat mindig a statikus adatterleten helyezi el a fordt. L 0 a 1 l 2 i 3 \0 4 Pldul a Lali karakterlnc egymst kvet, nvekv cm memria bjtokon gy helyezkedik el.

A karakterlnc karaktereiknt hasznlhat az escape szekvencia is. Pldul a


\t\t\Nv\\\\tCm\n\n

karakterlncot printf fggvny paramtereknt megadva


Nv\ Cm

jelenik meg utna kt soremelssel. A csak fehr karakterekkel elvlasztott karakterlnc konstansokat a fordt elemzsi fzisa alatt egyetlen karakterlncc egyesti:
Egy kett, hrom... Egy kett, hrom...

Tudjuk, hogy a karakterlnc konstans a folytatssor (\) jelet hasznlva tbb sorba is rhat:

68

TPUSOK S KONSTANSOK

printf(Ez igazbl egyetlen \ karakterlnc lesz.\n);

rjunk meg nhny karakterlncot kezel fggvnyt! Az unsigned strlen(char s[]) a paramter karakterlnc hosszt szolgltatja. Indtsunk egy vltozt zrustl, s indexeljnk elre vele a karakterlncon, mg a lnczr nullt el nem rjk! Ez az index a karakterlnc hossza is egyben.
unsigned strlen(char s[]){ unsigned i=0u; while(s[i]!=\0) ++i; return i; }

A void strcopy(char cl[], char forrs[]) a hozzrendelst valstja meg a karakterlncok krben azzal, hogy a forrs karakterlncot tmsolja a cl karaktertmbbe. Indexeljnk most is vgig egy vltozval a forrs karakterlncon a lezr zrusig! Kzben minden indexre rendeljk hozz a forrs tmbelemet a cl tmbelemhez! Vigyzzunk, hogy a lnczr nulla is tkerljn!
void strcopy(char cl[], char forrs[]){ int i=0; while((cl[i]=forrs[i])!=0) ++i; }

Javtsunk kicsit ezen a megoldson! A whilebeli relci elhagyhat, hisz a hozzrendels is mindig igaz (nem zrus), mg a forrs[i] nem lnczr zrus. Belthat, hogy a \0 karakter is tkerl, mert a while csak a hozzrendels vgrehajtsa utn veszi csak szre, hogy kifejezse hamiss (zruss) vlt. Teht:
while(cl[i]=forrs[i]) ++i;

Lssuk be, hogy a


while(cl[i++]=forrs[i]);

is tkletesen funkcionl, hisz Ellltja a forrs tmb iik elemt. Ezt hozzrendeli cl tmb iik elemhez. Az uttag ++ miatt mellkhatsknt kzben i rtke is n egyet. A legjobb megoldsunk teht:
void strcopy(char cl[], char forrs[]){ int i=0;

C programnyelv
while(cl[i++]=forrs[i]); }

69

A void strct(char s1[], char s2[]) egyesti a kt paramter karakterlncot s1be. Indexeljnk elre az s1en a lnczr nullig, s ezutn ide kell msolni az s2 karakterlncot! Belthat, hogy a helyes megoldshoz kt indexvltoz szksges, mert a msolsnl az egyiknek s1 lnczr nulljnak indextl kell indulnia, mg a msiknak s2n zrustl.
void strct(char s1[], char s2[]){ int i=0, j=0; while(s1[i]) ++i; while(s1[i++]=s2[j++]); }

A void strup(char s[]) nagybetss alaktja sajt helyn az s karakterlncot. Indexeljnk vgig az s karaktertmbn a lnczr zrusig! Egyegy pozcin meg kell vizsglni a tmbelem rtkt. Ha nem kisbet, akkor nem kell tenni semmit. Ha kisbet, t kell rni nagybetv. Ha csak az angol bc betivel foglalkozunk, akkor meg kell hatrozni a kisbet tmbelem eltolst ahoz kpest, s ezt az eltolst hozz kell adni Ahoz.
void strup(char s[]){ int i; for(i=0; s[i]; ++i) if(s[i]>=a&&s[i]<=z)s[i]=s[i]-a+A; }

Felttlenl meg kell emlteni, hogy a most megrt karakterlnc kezel fggvnyek kicsit ms nvvel, de azonos funkcival, s paramterezssel lteznek a szabvny knyvtrban. Hasznlatukhoz azonban a STRING.H fejfjlt be kell kapcsolni. A nvlista:
strlen strcopy strct strup strlen strcpy strcat strupr

Prbljuk ki a megrt fggvnyeinket egy rvid programmal! Krjnk be egy sort a szabvny bemenetrl! Alaktsuk t nagybetss! Tegyk el az ELEJE:, s mg a :VGE szveget! Jelentessk meg az eredmnyt! rjuk mg ki az eredeti s az egyestett karakterlnc hosszt! Helyszke miatt a fggvnyek teste helyett csak /* */eket kzlnk. A valsgban oda kell msolni a fggvnydefinci forrsszvegt is.

70

TPUSOK S KONSTANSOK

/* PELDA13.C: Karakterlnc kezelse */ #include <stdio.h> #define SOR 80 /* Bemeneti sor max. hossza. */ int getline(char s[],int n){ /* ... */ } unsigned strlen(char s[]){ /* ... */ } void strcopy(char cl[], char forrs[]){ /* ... */ } void strct(char s1[], char s2[]){ /* ... */ } void strup(char s[]){/* ... */ } void main(void){ int n; /* A bemeneti sor hossza. */ char sor[SOR+1], /* A bemeneti sor. */ egy[SOR*2]; /* Az egyestett karakterlnc. */ printf( "Adjon meg egy sort!\n" "Nagybetss alaktva megjelentetjk\n" "ELEJE: :VGE szvegekbe zrva.\n" "Kzljk az eredeti s a vgs hosszt is.\n"); n=getline(sor,SOR); strcopy(egy, "ELEJE:"); strct(egy, sor); strup(egy); strct(egy, ":VGE"); printf("%s\nEredeti hossz:%3d\nMostani hossz:%3d\n", egy, n, strlen(egy)); }

Megoldand feladatok: Ksztse el a kvetkez fggvnyeket, s prblja is ki ket egy rvid programmal! A void strlw(char s[]) kisbetss alaktja sajt helyn az s karakterlncot. Az int toup(int c) nagybetss alaktva visszaadja c rtkt. Az int tolw(int c) visszatrsi rtke c kisbetss alaktva. A void chdel(char s[], int c) kitrli az s karakterlncbl a sajt helyn a benne elfordul c karaktereket. Msoljuk t az s karakterlncot egy segd tmbbe gy, hogy az aktulis karakter csak akkor kerljn t, ha nem c! Az eredmny karakterlnc aztn visszamsoland sbe! Sokkal jobb, ha a feladatot segd tmb nlkl oldjuk meg, s a msolst a ck kihagysval rgtn az s tmbbe vgezzk. 4.5 Deklarci A deklarci megteremti a kapcsolatot az azonost s az objektum kztt, ill. rgzti pldul tbbek kztt az objektum adattpust. Ktfle deklarcirl beszlhetnk:

C programnyelv

71

A defincis deklarcival (definci) helyet foglaltatunk az objektumnak a memriban, s esetleg kezdrtkkel is inicializljuk. Ilyen deklarci egy azonostra csak egyetlen egy ltezhet az egsz forrsprogramban. A referencia deklarci (deklarci) nem foglal memrit az objektumnak, de tudatja a fordtval, hogy van egy ilyen azonostj, adattpus, stb. objektum a programban. Ebbl a deklarcibl egymsnak ellent nem mondan - tbb is ltezhet ugyanarra az objektumra. Szlnunk kell mg deklarcis pontrl! Ez az azonost deklarcijnak helye a forrsprogramban. Az azonost deklarcis pontja eltt leglisan nem rhet el a forrskdban. Tudjuk azt is, hogy a deklarci elhelyezse s maga a deklarci meghatrozza az objektum attribtumait: tpus, trolsi osztly, hatskr, lettartam stb. A ksrleti definci fogalmt az ANSI szabvny vezette be. Brmely kls adatdeklarci, melynek nincs trolsi osztly specifiktora s nincs explicit inicializtora, ksrleti defincinak tekintend. Ha a deklarlt azonost aztn feltnik egy ksbbi definciban, akkor a ksrleti defincit extern trolsi osztlynak kell venni. Ms szval, a ksrleti definci egyszer referencia deklarci. Ha a fordtsi egysg vgig sem tallkozunk defincival a ksrleti defincis azonostra, akkor a ksrleti definci teljes definciv vlik tiszta zrus kezdrtk objektummal.
int int int int x; x; /* OK leglis, hisz nem mondtunk jat. */ y; y = 4; /* OK. Most specifikltuk, hogy y-t 4-re kell inicializlni. */ int z = 4; int z = 6; /* HIBS, mert mindkett inicializlna. */

A deklarlhat objektumok a kvetkezk: vltozk,

72 fggvnyek, tpusok, tpusok tmbjei, enum konstansok s cmkk.

TPUSOK S KONSTANSOK

Deklarlhatk mg a kvetkez ksbbi fejezetekben trgyalt objektumok is: struktra, uni s utasts cmkk, struktra s unitagok, valamint elfeldolgoz makrk. A deklartor szintaktika rekurzivitsa miatt egszen komplex deklartorok is megengedettek. Ezt el szoks kerlni tpusdefincival (typedef). A deklarci metanyelvi lersa a kvetkez:
deklarci: deklarci-specifiktorok<init-deklartorlista> deklarci-specifiktorok: trolsi-osztly-specifiktor<deklarci-specifiktorok> tpusspecifiktor<deklarci-specifiktorok> init-deklartorlista: init-deklartor init-deklartorlista, init-deklartor init-deklartor: deklartor deklartor=inicializtor inicializtor: hozzrendels-kifejezs {inicializtorlista} {inicializtorlista ,} inicializtorlista: inicializtor inicializtorlista, inicializtor trolsi-osztly-specifiktor: auto register extern static typedef tpusspecifiktor: void char short int long float double

C programnyelv
signed unsigned const volatile struktra-vagy-uni-specifiktor enum-specifiktor typedef-nv typedef-nv: azonost deklartor: <mutat>direkt-deklartor direkt-deklartor: azonost (deklartor) direkt-deklartor [<konstans-kifejezs>] direkt-deklartor (paramter-tpus-lista) direkt-deklartor (<azonostlista>) konstans-kifejezs: feltteles-kifejezs azonostlista: azonost azonostlista, azonost

73

A mutatkkal, a struktrval, az unival, a fggvnyekkel, az utasts cmkkkel s a makrkkal ksbbi szakaszokban s fejezetekben foglalkozunk, gy a vonatkoz metanyelvi lersok is ott tallhatk. A trolsi-osztly-specifiktorokat teljes rszletessggel majd egy ksbbi fejezetben taglaljuk, de kzlk kettrl (auto s extern) mr sz volt a BEVEZETS S ALAPISMERETEK szakaszban. A kvetkez fejezet nmi elzetes kpet ad a typedefrl. A tpusspecifiktorokat alaptpusokra s tpusmdostkra bontottuk korbban. A tpusmdostk s alaptpusok megengedett, egyttes hasznlatt e szakasz elz fejezeteiben tisztztuk. A const s a volatile viszont brmely alaptpussal s tpusmdostval egytt szinte korltozs nlkl hasznlhat. A deklartor egyedi azonostt hatroz meg. Mikor az azonost megjelenik egy vele egyeztpus kifejezsben, akkor a vele elnevezett objektum rtkt eredmnyezi. A konstans-kifejezs mindig fordtsi idben is meghatrozhat konstans rtkk rtkelhet ki, azaz rtke bele kell hogy frjen a tpus brzolsi hatraiba. A konstans kifejezs brhol alkalmazhat, ahol konstans egybknt hasznlhat. A konstans kifejezs operandusai lehetnek egsz konstansok, karakter konstansok, lebegpontos konstansok, enumerto-

74

TPUSOK S KONSTANSOK

rok, tpusmdost szerkezetek, sizeof kifejezsek, stb. A konstans kifejezs azonban a sizeof opertor operandustl eltekintve nem tartalmazhatja a kvetkez opertorok egyikt sem: hozzrendels, vessz, dekrementls, inkrementls s fggvnyhvs. 4.5.1 Elemi tpusdefinci (typedef) Nem tartozik igazn ide a tpusdefinci trgyalsa, de miutn a typedef kulcsszt a trolsi osztly specifiktor helyre kell rni, itt adunk rla egy rvid ismertetst. A typedef kulcsszval nem j adattpust, hanem j adattpus specifiktort definilunk. Legyen sz pldul az
auto long int brigi;

defincirl, mely szerint a brigi egy 32 bites, signed long int tpus, loklis lettartam objektum azonostja. Hasznljuk most az auto kulcssz helyett a typedef-et, s az azonostt rjuk t nagybetsre!
typedef long int BRIGI;

, ahol a BRIGI azonost nem kpez futsidej objektumot, hanem egy j tpusspecifiktor csak. A programban ezutn a BRIGI tpusspecifiktorknt alkalmazhat deklarcikban. Pldul az
extern BRIGI fizetni;

ugyanolyan hats, mint az


extern long int fizetni;

Ez az egyszer plda megoldhat lenne a


#define BRIGI long

mdon is, a typedef-fel azonban az egyszer szveghelyettestsnl komplexebb alkalmazsok is thidalhatk. A typedef nem hoz ltre j tpust tulajdonkppen, csak ltez tpusokra krelhat vele j kulcssz. Komplexebb deklarcik egyszerstsre val.

C programnyelv

75

A tpusdefinci megbonyoltsra a ksbbiekben mg visszatrnk! Meg kell jegyeznnk azonban annyit, hogy a typedef-fel ltrehozott tpusspecifiktor nem hasznlhat a deklarciban ms tpusspecifiktorokkal egytt! Legfeljebb a const s a volatile mdostk alkalmazhatk r! Pldul
unsigned BRIGI keresni; const BRIGI kaba = 2; /* HIBS. */ /* OK */

76

MVELETEK S KIFEJEZSEK

5 MVELETEK S KIFEJEZSEK
A mveleteket a nyelv opertorokkal (mveleti jelekkel) valstja meg. A mveletek lehetnek: Egyoperandusosak. Alakjuk opertor operandus, ahol az operandus az a kifejezs, melyen az egyoperandusos mveletet el kell vgezni. Pldul: 6, vagy a sizeof(int) stb. Ktoperandusosak, azaz operandus1 opertor operandus2 formjak. Pldul: a + b. Hromoperandusosak: A C-ben egyetlen ilyen mvelet van, az n. feltteles kifejezs. Pldul: (a > b) ? a : b rtke a, ha a > b s b msklnben.
opertor: (a kvetkezk egyike!) [ ] ( ) . -> ++ -- & * + - ~ ! sizeof / % << >> < > <= >= == != = ^ | && || ?: *= /= += -= %= <<= >>= &= ^= |= ,

A kifejezs opertorok, operandusok (s elvlaszt-jelek) sorozata, mely az albbi tevkenysgek valamilyen kombincijt valstja meg: rtket szmt ki. Objektumot vagy fggvnyt r el. Mellkhatst generl. A kifejezsbeli operandusokat elsdleges kifejezsnek nevezik.
elsdleges-kifejezs: azonost konstans karakterlnc (kifejezs) kifejezs: hozzrendels-kifejezs kifejezs, hozzrendels-kifejezs

A konstansokat, a karakterlncot trgyaltuk a TPUSOK S KONSTANSOK szakaszban, a hozzrendels-kifejezst definilni fogjuk a hozzrendels opertoroknl. Az azonost lehet brmilyen egsz vagy lebegpontos tpus. Lehet enum, tmb, mutat, struktra, uni, vagy fggvny tpus. Lehet teht: vltoz azonost belertve az indexel opertort is, azaz az azonost[kifejezs]-t is, s az uni, ill. a struktratagokat, vagy

C programnyelv

77

fggvnyhvs, azaz azonost a fggvnyhvs opertorral ( azonost() ), melynek tpusa mindig a fggvny ltal visszaadott rtk tpusa lesz. sszestve: az azonostnak balrtknek vagy fggvnyhvsnak kell lennie. A kifejezs kirtkelse bizonyos konverzis, csoportost, asszociatv s prioritsi (precedencia) szablyokat kvet, mely fgg a hasznlt opertoroktl, a ( ) prok jelenlttl s az operandusok adattpustl. A kifejezsek klnflk lehetnek: elsdleges kifejezs (primary), uttag kifejezs (postfix), egyoperandusos kifejezs (unary), eltag kifejezs (cast), hozzrendels kifejezs stb. Figyeljk meg, hogy a kifejezsek elnevezse - az elsdleges kifejezstl eltekintve - a vele hasznlatos opertorok szerint trtnik!
uttag-kifejezs: elsdleges-kifejezs uttag-kifejezs[kifejezs] uttag-kifejezs(<kifejezslista>) uttag-kifejezs.azonost uttag-kifejezs->azonost uttag-kifejezs++ uttag-kifejezs kifejezslista: hozzrendels-kifejezs kifejezslista , hozzrendels-kifejezs

78

MVELETEK S KIFEJEZSEK

egyoperandusos-kifejezs: uttag-kifejezs ++ egyoperandusos-kifejezs -- egyoperandusos-kifejezs egyoperandusos-opertor eltag-kifejezs sizeof(egyoperandusos-kifejezs) sizeof(tpusnv) egyoperandusos-opertor: ( a kvetkezk egyike!) &*+-~! eltag-kifejezs: egyoperandusos-kifejezs (tpusnv) eltag-kifejezs tpusnv: tpusspecifiktor-lista<absztrakt-deklartor> absztrakt-deklartor: mutat <mutat><direkt-absztrakt-deklartor> direkt-absztrakt-deklartor: (absztrakt-deklartor) <direkt-absztrakt-deklartor>[<konstans-kifejezs>] <direkt-absztrakt-deklartor>(<paramter-tpus-lista>)

A tpusnv az adattpus tpusneve. Szintaktikailag az adott tpus objektum olyan deklarcija, melybl hinyzik az objektum neve. A hozzrendels-kifejezst, melyrl most csak annyit jegyznk meg, hogy nem balrtk, majd a hozzrendelsi mveleteknl ismertetjk! 5.1 Aritmetikai mveletek (+, -, *, / s %) Kzlk a legmagasabb prioritsi szinten az egyoperandusos, jobbrl balra kt eljel opertorok vannak. Ltezik a
- eltag-kifejezs

s a szimmetria kedvrt a
+ eltag-kifejezs.

Az eljel opertort kvet eltag kifejezsnek aritmetikai tpusnak kell lennie, s az eredmny az operandus rtke (+), ill. annak -1-szerese (-). A + mvelet egsz operandust egszellptetsnek (integral promotion) veti al a fordt, s gy az eredmny tpusa az egszellptets vgrehajtsa utn kpzett tpus. A mveletet megelzheti implicit tpuskonverzi, s egsz operandus esetn az eredmny az operandus rtknek kettes komplemense. Az egszellptetssel az implicit tpuskonverzi kapcsn rgtn foglalkozunk!

C programnyelv

79

A tbbi aritmetikai opertor mind ktoperandusos, melyek kzl a szorzs (*), az oszts (/) s a modulus (%) magasabb prioritsi szinten van, mint az sszeads (+) s a kivons (-). A szorzst, az osztst s a modulust multiplikatv opertoroknak, az sszeadst s a kivonst additv opertoroknak is szoks nevezni. 5.1.1 Multiplikatv opertorok (*, / s %)

multiplikatv-kifejezs: eltag-kifejezs multiplikatv-kifejezs * eltag-kifejezs multiplikatv-kifejezs / eltag-kifejezs multiplikatv-kifejezs % eltag-kifejezs

Nzzk a mveletek pontos szablyait! A multiplikatv opertorok mind balrl jobbra csoportostanak. Mindhrom opertor operandusainak aritmetikai tpusaknak kell lennik. A % opertor operandusai radsul csak egsz tpusak lehetnek. Ha az operandusok klnbz aritmetikai tpusak, akkor a mvelet elvgzse eltt implicit konverzit hajt vgre a fordt. Az eredmny tpusa ilyenkor a konvertlt tpus. Miutn a konverzinak nincsenek tl vagy alulcsordulsi felttelei, rtkveszts kvetkezhet be, ha az eredmny nem fr el a konverzi utni tpusban. A / s a % msodik operandusa nem lehet zrusrtk, mert ez fordtsi vagy futsidej hibhoz vezet. Ha a / s a % mindkt operandusa egsz, de a hnyados nem lenne az, akkor: Ha a kt operandus - mondjuk op1 s op2 - rtke azonos eljel vagy unsigned, akkor az op1/op2 hnyados az a legnagyobb egsz, ami kisebb, mint az igazi hnyados s az op1%op2 osztsi maradk op1 eljelt rkli meg:
3 % 2 1 (-3) % (-2) -1

3 / 2 1 (-3) / (-2) 1

Ha op1 s op2 ellenkez eljel, akkor az op1/op2 hnyados az a legkisebb egsz, ami nagyobb az igazi hnyadosnl. Az op1%op2 osztsi maradk most is op1 eljelt rkli meg:
(-3) / 2 -1 3 / (-2) -1 (-3) % 2 -1 3 % (-2) 1

80

MVELETEK S KIFEJEZSEK

Ksztsnk programot, ami beolvas egy ngyjegy vszmot, s eldnti rla, hogy szkve, vagy sem! A Gergelynaptr szerint szkv minden, nggyel maradk nlkl oszthat v. Nem szkv a kerek vszzad, de a 400zal maradk nlkl oszthatk mgis azok. 1. Olvassunk be a szabvny bemenetrl egy maximlisan ngy karakteres sort! 2. Ha a bejtt karakterlnc hossza nem pontosan ngy, akkor krjk be jra! 3. Ellenrizzk le, hogy a karakterlnc minden pozcija numerikuse! Ha nem, jra bekrend. rjunk int nume(char s[]) fggvnyt, mely 1et (igazat) ad vissza, ha a paramter karakterlnc tiszta numerikus, s zrust (hamisat), ha nem!
int nume(char s[]){ int i; for(i=0; s[i]; ++i) if(s[i]<0||s[i]>9) return 0; return 1; }

4. t kne konvertlni a numerikus karakterlncot fixpontos belsbrzols egssz (int nn)! A mdszer a kvetkez:
n=(s[0]-0)*1000+(s[1]-0)*100+(s[2]-0)*10+ (s[3]-0);

Ezt ugye ciklusban, ahol i s n zrustl indul, s i egyesvel haladva vgigjrja a numerikus karakterlncot, gy kne csinlni:
n=n*10+(s[i]-0);

rjunk int atoi(char s[]) fggvnyt, mely megvalstja ezt a konverzit, s n lesz a visszaadott rtke! Az talaktst vgezze az els nem konvertlhat karakterig! Engedjk meg, hogy a numerikus karakterlnc elejn fehr karakterek s eljel is lehessen! Ha az eljelet elhagyjk, akkor legyen a szm pozitv!
int atoi(char s[]){ int i=0, n=0; int elojel=1; /* Alaprtelmezs: pozitv. */ /* A karakterlnc eleji fehr karakterek tlpse: */ while(s[i]== ||s[i]==\n||s[i]==\t) ++i; /* Eljel: */ if(s[i]=='+'||s[i]=='-') if(s[i++]=='-') elojel=-1; /* Konverzi: */ for(;s[i]>='0'&&s[i]<='9';++i) n=10*n+s[i]-'0'; return(elojel*n); }

C programnyelv

81

Megemltend, hogy pontosan ilyen prototpus, nev s funkcij fggvny ltezik a szabvny knyvtrban is, de bekapcsoland hozz a szabvnyos STDLIB.H fejfjl. A knyvtrban van atol rutin, mely long g, s van atof, mely doublel alaktja numerikus karakterlnc paramtert. Jhet a program, de helyszke miatt a fggvnyek definciit nem ismteljk meg!
/* PELDA14.C: A ngyjegy vszm szkv-e? */ #include <stdio.h> #define MAX 80 int getline(char s[], int n); int nume(char s[]); int atoi(char s[]); void main(void){ int ev = 0; /* Elfogadhatatlan rtkrl indul. */ char s[MAX+1]; /* Az input puffer. */ printf("A ngyjegy vszm szkv-e?\n"); while(ev<1000 || ev>9999){ printf("Adjon meg egy vszmot!\n"); if(getline(s,4)==4&&nume(s)) ev=atoi(s); else printf(Formailag hibs bemenet!\n); } if(ev%4==0&&ev%100!=0||ev%400==0) printf("%4d szkv.\n", ev); else printf("%4d nem szkv.\n", ev); }

Megoldand feladatok: Ksztsen szmot ler karakterlncok formai ellenrzst vgz fggvnyeket az atoi alapjn, melyek helyes esetben 1et (igaz) adnak vissza, s a hibt zrussal (hamis) jelzik! A lnc eleji fehr karaktereket t kell lpni. A szm vgt a karakterlnc vge, vagy jabb fehr karakter kvetkezse mutatja. Az int egesze(char s[]) a decimlis egsz konstans rsszablyt ellenrzi a paramter karaktertmbn. Az int hexae(char s[]) megvizsglja, hogy paramtere hexadecimlis szme. Az int ell210e(char s[]) teszteli, hogy s 2 s 10 kztti alap szm e. A szmrendszer alapjt fordtsi idben vltoztatni (#define) lehet! Az int ellae(char s[], int alap) ugyanazt teszi, mint az ell210e, de a 2 s 10 kztti alapot futsi idben paramterknt kapja meg.

82

MVELETEK S KIFEJEZSEK

Az int ella36e(char s[], int alap) egyezik ellaevel, de az alap 2 s 36 kztti lehet. A tznl nagyobb alap szmrendszerek esetben a szmjegyeket az angol bc betivel jelljk rendre, vagyis 10=A, 11=B stb. A 36os korltozs ebbl fakad. Ksztsen konverzis fggvnyeket is a leellenrztt karakterlncokra az atoi mintjra, s a konvertlt rtk legyen a rutinok visszatrsi rtke! A double atofix(char s[]) az eljeles, legfeljebb egsz s trt rszbl ll vals rtket alaktja doublel. A long atoh(char s[]) hexadecimlis karakterlncot alakt egssz. A long ato36(char s[], int alap) a legfeljebb 36 alap szmrendszerbeli lncot konvertlja egssz. 5.1.2 Additv opertorok (+ s -)

additv-kifejezs: multiplikatv-kifejezs additv-kifejezs + multiplikatv-kifejezs additv-kifejezs - multiplikatv-kifejezs

Az additv opertorok csoportostsa is balrl jobbra trtnik. Operandusaik az aritmetikai rtkeken tl mutatk is lehetnek. A mutataritmetikt majd a mutatk kapcsn ismertetjk! Aritmetikai operandusok esetn az eredmny a kt operandus rtknek sszege (+), ill. klnbsge (). Egsz vagy lebegpontos operanduson a mvelet implicit tpuskonverzit is vgezhet, ha szksges. Ilyenkor az eredmny tpusa a konvertlt tpus. Miutn a konverzinak nincsenek tl vagy alulcsordulsi felttelei, rtkveszts kvetkezhet be, ha az eredmny nem fr el a konverzi utni tpusban. 5.1.3 Matematikai fggvnyek A matematikai fggvnyek nem rszei a C nyelvnek. Nyilvnval viszont, hogy kifejezsek kpzsekor szksg lehet rjuk. A C filozfija szerint a matematikai fggvnyek csaldjt is a szabvny knyvtrban kell elhelyezni, mint ahogyan a szabvny bemenet s kimenet kezelst vgz rutinokat. Az ANSI szabvny pontosan rgzti ezeket a knyvtri funkcikat, gy brmilyen szabvnyos C fordt s opercis rendszer szmra kompatibi-

C programnyelv

83

lis formban lteznik kell. Magyarn: azok a programok, melyek az opercis rendszerrel val kapcsolatukat a szabvny knyvtron t valstjk meg, minden vltoztats nlkl tvihetk az egyik szmtgprl a msikra, az egyik opercis rendszerbl a msikba. Ezek az gy nevezett portbilis programok. A szabvny knyvtr fggvnyeit, tpusait s makrit szabvnyos fejfjlokban deklarltk. Ezek kzl nhnnyal mr tallkoztunk, msokkal meg mg nem: ASSERT.H CTYPE.H LIMITS.H TIME.H ERRNO.H FLOAT.H STDLIB.H ISO646.H STRING.H LOCALE.H MATH.H WCHAR.H WCTYPE.H SETJMP.H SIGNAL.H

STDARG.H STDDEF.H STDIO.H

A matematikai fggvnyek prototpusai a MATH.H fejfjlban helyezkednek el, gy hasznlatuk eltt ez a fejfjl bekapcsoland!
#include <math.h>

Nem kvnjuk felsorolni s rszletezni az sszes fejfjlt, az sszes fggvnyt, csak nhny fontosabbat emltnk meg kzlk. Az olvastl azonban elvrjuk, hogy a programfejleszt rendszere segtsgbl a tovbbi fejfjlokrl s rutinokrl is tjkozdjk. A matematikai fggvnyek double rtket szolgltatnak, s nhny kivteltl eltekintve, paramtereik is double tpusak. A matematikbl ismeretes korltozsok termszetesen rvnyben maradnak rjuk. A trigonometrikus fggvnyek paramtere, ill. inverzeik visszaadott rtke radinban rtend. Nhnyat felsorolunk a teljessg ignye nlkl! sin(x) cos(x) tan(x) asin(x) acos(x) atan(x) exp(x) log(x) x szinusza. x koszinusza. x tangense. 1<=x<=1 rkusz szinusza. Az rtkkszlet: [/2, /2]. 1<=x<=1 rkusz koszinusza. Az rtkkszlet: [0, ]. x rkusz tangense. Az rtkkszlet: [/2, /2]. Az ex exponencilis fggvny. x>0 termszetes alap logaritmusa. (ln(x)).

84 log10(x) pow(x, y) sqrt(x) floor(x) fabs(x) fmod(x, y)

MVELETEK S KIFEJEZSEK x>0 tzes alap logaritmusa. (lg(x)). Az xy hatvnyfggvny. Hiba, ha x=0 s y<=0, ill. ha x<0 s y rtke nem egsz. x>=0 ngyzetgyke. Az xnl nem nagyobb, legnagyobb egsz szm. Az x abszolt rtke. y!=0 estn x/y oszts lebegpontos maradka, mely xszel egyez eljel.

A szabvny knyvtri fggvnyek, gy a matematikaiak is, a hibt gy jelzik, hogy valamilyen specilis rtket (HUGE_VAL, zrus stb.) adnak vissza, s belltjk a UNIXtl rklt, globlis
extern int errno;

(hibaszm) vltozt a hiba kdjra. A hibakdok az ERRNO.H fejfjlban definilt, egsz, nem zrusrtk szimbolikus llandk. A HUGE_VAL a legnagyobb, pozitv, mg brzolhat double rtk. A matematikai rutinok az rtelmezsi tartomny hibt EDOM rtk errnoval, s a fordttl is fgg fggvny visszatrsi rtkkel jelzik. rtkkszlet problma esetn az errno ERANGE. A fggvny visszatrsi rtk tlcsordulskor eljel helyes HUGE_VAL, ill. alulcsordulskor zrus. Az rtelmezsi tartomny hiba akkor fordul el, ha a fggvny aktulis paramternek rtke nincs benn az rtelmezsi tartomnyban. rtkkszlet hiba egyrtelmen az, ha az eredmny nem brzolhat double rtkknt. Pldul az sqrt(1.) hatsra az errno EDOM, s a visszakapott rtk negatv HUGE_VAL. Megoldand feladatok: Ksztend a kzpiskols fggvnytblzatok mintjra lapozhatan: egy logaritmustbla s egy szinusztbla. 5.2 Relci opertorok ( >, >=, <, <=, == s !=) A relci opertorok prioritsa - eltekintve az egyoperandusos mveletektl - az aritmetikai s a logikai opertorok kztt helyezkedik el. A

C programnyelv

85

relci opertorok kt prioritsi szintet kpeznek, ahol az igazi relcik (>, >=, < s <=) prioritsa magasabb az egyenlsgi relciknl (== s !=). Az sszes relci az els operandus rtkt hasonltja a msodikhoz, s a relci rvnyessgt vizsglja. Az eredmny logikai rtk (int tpus), mely 1, ha a relci igaz s 0, ha nem. A defincik:
relcis-kifejezs: eltols-kifejezs relcis-kifejezs < eltols-kifejezs relcis-kifejezs > eltols-kifejezs relcis-kifejezs <= eltols-kifejezs relcis-kifejezs >= eltols-kifejezs egyenlsgi-kifejezs: relcis-kifejezs egyenlsgi-kifejezs == relcis-kifejezs egyenlsgi-kifejezs != relcis-kifejezs

Az eltols-kifejezst a bitenknti eltols opertoroknl definiljuk! A relcik operandusai egsz, lebegpontos, vagy mutat tpusak. Az operandusok tpusa klnbzhet. Az opertorok implicit tpuskonverzit is vgrehajthatnak aritmetikai operandusaikon a mvelet elvgzse eltt. Ne feledjk, hogy a
kifejezs != 0

relci mindig rvidthet


kifejezs

mdon, mert a nyelvben a nem zrus rtk logikai igaznak minsl. Pldaknt tekintsk meg jra a korbbi szakaszokban ismertetett atoi s getline fggvnyeket! 5.3 Logikai mveletek ( !, && s ||) Kzlk a legmagasabb prioritsi szinten az egyoperandusos, jobbrl balra kt, logikai nem opertor van, melynek alakja:
! eltag-kifejezs

, ahol az eltag-kifejezs operandusnak egsz, lebegpontos, vagy mutat tpusnak kell lennie. Az eredmny mindenkppen int tpus, s az operandus logikai negcija. Az eredmny 0, ha az operandus rtke nem zrus, ill. 1, ha az operandus rtke zrus. Ez utbbi mondatrsz biztostja, hogy a
kifejezs == 0

mindenkor rvidthet

86
! kifejezs

MVELETEK S KIFEJEZSEK

mdon. Pldul a multiplikatv opertoroknl ismertetett program rszlet


if( ev%4 == 0 && ev%100 != 0 || ev%400 == 0)

utastsa gy rvidthet:
if( !(ev%4) && ev%100 || !(ev%400))

Kt ktoperandusos logikai mvelet van a nyelvben a logikai s (&&) s a logikai vagy (||), melyek prioritsa alacsonyabb a relciknl s a bit szint mveleteknl. A logikai s prioritsa radsul magasabb, mint a logikai vagy. Mindkt mvelet balrl jobbra csoportost. Egyik opertor sem hajt vgre implicit tpuskonverzit operandusain, ehelyett zrushoz viszonytva rtkeli ki ket. Az eredmny int tpus (1 - igaz s 0 - hamis).
logikai-s-kifejezs: vagy-kifejezs logikai-s-kifejezs && vagy-kifejezs logikai-vagy-kifejezs: logikai-s-kifejezs logikai-vagy-kifejezs || logikai-s-kifejezs

A vagy-kifejezs defincijt a bit szint mveleteknl talljuk meg! A K1&&K2 kifejezs eredmnye igaz (1), ha K1 s K2 egyike sem zrus. A K1||K2 kifejezs igaz (1), ha K1 s K2 valamelyike is nem zrus. Msklnben K1&&K2 s K1||K2 eredmnye hamis (0). Mindkt opertor esetben garantlt a balrl jobbra trtn vgrehajts. Elszr K1et rtkeli ki a fordt az esetleges sszes mellkhatsval egytt, de: K1&&K2 esetn, ha K1 zrus, az eredmny hamis (0), s K2 kirtkelse nem trtnik meg. K1||K2 kifejezsnl, ha K1 nem zrus, az eredmny igaz (1) lesz, s K2 kirtkelse itt sem zajlik le. Ha valami elbbre val, vagy mindenkpp szeretnnk, hogy megtrtnjen, akkor azt a bal oldali operandusba kell bepteni. Pldul a PELDA10.Cben megrt getline for ciklusnak felttele nem vletlenl
i<n && (c=getchar())!=EOF && c!=\n

sorrend, hisz elszr azt kell biztostani, hogy a paramter karaktertmbt ne rhassa tl a fggvny. Ez nem kerlhet htrbb a kifejezsben. Aztn a kvetkez karaktert elbb be kell olvasni a bemenetrl, de min-

C programnyelv

87

mindennek vge van fjlvg esetn. Itt sincs rtelme a felcserlsnek, mert felesleges vizsglgatni a fjlvget, hogy soremelse. A relcik kzti s mveletek miatt ltszik, hogy balrl jobbra trtnik az operandusok kirtkelse, s ha ekzben az egyik hamis lesz, teljesen felesleges lenne tovbbfolytatni a kirtkelst. 5.4 Implicit tpuskonverzi s egszellptets Ha ktoperandusos (pldul aritmetikai) mveleteknl klnbzik a kt operandus tpusa, akkor a mvelet elvgzse eltt a fordt bels konverzit (talaktst) hajt vgre. ltalban a pontosabb operandus tpusra konvertlja a msikat. A ktoperandusos mvelet eredmnynek tpusa a konvertlt tpus lesz. Ezt a fajta konverzit szabvnyosnak, szoksosnak is nevezik. A szablyok nem prioritsi sorrendben a kvetkezk: 1. Ha az egyik operandus tpusa long double, akkor a msikat is long double tpusv konvertlja a fordt. 2. Ha az elz pont nem teljesedik, s az egyik operandus double, akkor a msik is az lesz. 3. Ha az elz kt felttel egyike sem valsul meg, s az egyik operandus float, akkor a msikat is azz konvertlja a fordt. 4. Ha az elz hrom felttel egyike sem teljesl (egyik operandus sem lebegpontos!), akkor egszellptetst hajt vgre a fordt az operandusok rtkn, ha szksges, s aztn: Ha az egyik operandus unsigned long, akkor a msik is azz alakul. Ha az elz pont nem teljesl, s az egyik operandus long, a msik pedig unsigned int, akkor mindkt operandus rtkt long, vagy unsigned long tpusv konvertlja a fordt. Ha az unsigned int teljes rtktartomnya brzolhat longknt, akkor a vlaszts long, msklnben pedig unsigned long. Ha az int 16, s a long 32 bites, akkor 1L<1U. Hisz az elmondottak szerint az unsigned int longg alakul, s 1L<1L. Ha az int 32 bites, akkor 1L>1UL, mert a 1L 111111111111111111111111111111112 binrisan, unsigned longg alaktva ugyanez marad, s ez sokkal nagyobb 000000000000000000000000000000012nl. Ha az elz pontok nem teljeslnek, s az egyik operandus long, akkor a msik is az lesz. Ha nem teljeslnek az elz pontok, s az egyik operandus unsigned int, akkor a msik is azz alakul.

88

MVELETEK S KIFEJEZSEK

Ha az elz pontok nem teljeslnek, akkor minkt operandus int az rvnybe lpett az egszellptets (integral promotion) miatt. A signed vagy unsigned char, short int, vagy bitmez objektumok, ill. az enum tpusak hasznlhatk kifejezsben ott, ahol bennk egsz llhat. Ha az eredeti tpus minden lehetsges rtkt int kpes reprezentlni, akkor az rtket int tpusv konvertlja a fordt, msklnben unsigned int-t. Az egszellptetsi folyamat garantlja, hogy a konverzi eltti s utni rtk ugyanaz marad. A konvertlt rtk unsigned eredeti tpusbl 0X00 feltltssel, signed eredeti tpusbl viszont eljel kiterjesztssel kszl a fels bjtokba. Tpus char Konvertlva int Mdszer Az alaprtelmezett char tpustl fggen eljel kiterjeszts van (signed) vagy 0X00 kerl a magasabb helyirtk bjt(ok)ba (unsigned). A fels bjt(ok) 0X00 feltltsek. Eljel kiterjeszts van a fels bjt(ok)ba. Ugyanaz az rtk eljel kiterjesztssel. Ugyanaz az rtk.

unsigned char signed char short int enum

int int int int

unsigned short unsigned int Ugyanaz az rtk 0X00 feltltssel.

12. bra: Egszellptets

Ne feledjk azonban el, hogy a konverzi mindig fggvnyhvst jelent (gpid!), azaz szksgtelenl ne alkalmazzuk! Csak rtelmes tpuskonverzikat valst meg a fordt. Pldul az f + i sszeads vgrehajtsa eltt - feltve, hogy f float s i int tpus - i rtke (s nem i maga!) float-t alakul. Az rtelmetlen lebegpontos kifejezs indexben mg csak megvalsul gy, hogy a kifejezs rtke trt rszt levgja a fordt
#include <stdio.h> void main(void){ int t[] = {2,3,4,5,6,7}; float f=1.75; printf("%d\n",t[f]); }

, azaz 3 lesz az eredmny.

C programnyelv

89

Numerikus karakterlnc azonban sohasem alakul automatikusan aritmetikai rtkk. Ehhez valamilyen konverzis fggvnyt kell hasznlni. Az STDLIB.Hbeli atoirl, atolrl s atofrl volt mr sz! 5.5 Tpusmdost szerkezet Az implicit (szoksos, szabvnyos stb.) konverzikon tl magunk is kiknyszerthetnk (explicit) tpuskonverzit a
(tpusnv) eltag-kifejezs

alak, a BEVEZETS S ALAPISMERETEK szakaszban megismert tpusmdost szerkezet alkalmazsval. Ltjuk, hogy a tpusmdost szerkezet egyoperandusos, s ez ltal magas priorits mvelet. A definciban a tpusnv a cltpus, s az eltag-kifejezs rtkt erre a tpusra kell konvertlni. Az eltag-kifejezst gy konvertlja a fordt, mintha az rtket egy tpusnv tpus vltoz venn fel. Az explicit tpuskonverzi teht a hozzrendelsi konverzi szablyait kveti. A leglis tpusmdostsok: Cltpus egsz void Potencilis forrsok brmilyen egsz vagy lebegpontos tpus, vagy mutat brmilyen tpus

lebegpontos brmilyen aritmetikai tpus Pldaknt vegyk a matematikai fggvnyek kzl a ngyzetgykt, azaz:
#include <math.h> double sqrt(double x);

Programunkban van egy int tpus n vltoz, akkor az n+26 pozitv gykt az
sqrt(double(n+26))

fggvnyhvssal kaphatjuk meg. Brmilyen azonost, vagy kifejezs tpusa mdosthat voidd. A tpusmdostsnak alvetett azonost, vagy kifejezs nem lehet azonban void. A void fggvny hvst pldul hiba tpusmdostjuk int-re, a semmibl nem lehet egszet csinlni. A voidd mdostott kifejezs rtke nem kpezheti hozzrendels trgyt. Hasonlan az explicit tpusmdosts eredmnye nem fogadhat el balrtkknt hozzrendelsben.

90

MVELETEK S KIFEJEZSEK

Kifejezst csak olyan helyen mdosthatunk void-d, ahol az rtkre nincs szksg. Pldul nincs szksg a bejv gombnyomsra:
printf(A folytatshoz ssn Entert-t! ); (void)getchar();

5.6 sizeof opertor A BEVEZETS S ALAPISMERETEK szakaszbl ismert sizeof egyoperandusos, jobbrl balra kt, magas priorits mvelet, mely mindig az operandusa trolshoz szksges memria mennyisgt szolgltatja bjtban. Az eredmny size_t tpus egsz rtk. Az STDDEF.H fejfjlban megtekintve a tpust tbbnyire azt ltjuk, hogy unsigned int. tpus rtelmezse fordttl fgg tulajdonkppen! A size_t Kt klnbz alakja van az opertornak:
sizeof(egyoperandusos-kifejezs) sizeof(tpusnv)

sizeof(egyoperandusos-kifejezs) esetn az egyoperandusos kifejezs tpust a fordt a kifejezs kirtkelse nlkl hatrozza meg, azaz ha az operandus tmbazonost, az egsz tmb bjtokban mrt helyfoglalshoz jutunk. Pldul a tomb tmb elemszma a kvetkez konstrukcival llapthat meg:
sizeof(tomb) / sizeof(tomb[0])

A sizeof nem hasznlhat fggvnyre, vagy nem teljes tpus kifejezsre, ilyen tpusok zrjelbe tett nevre, vagy olyan balrtkre, mely bitmez objektumot jell ki. A sizeof azonban btran alkalmazhat elfeldolgoz direktvkban is!
#define MERET sizeof(int)*3

5.7 Inkrementls (++), dekrementls (--) s mellkhats Ezek az opertorok mind egyoperandusosak, s ezrt magas prioritsak. Mindkt opertor ltezik uttag
uttag-kifejezs++ uttag-kifejezs--

s eltag mveletknt:
++ egyoperandusos-kifejezs -- egyoperandusos-kifejezs

C programnyelv

91

Az inkrementl s dekrementl kifejezsben az uttag- vagy az egyoperandusos-kifejezsnek skalr (aritmetikai vagy mutat) tpusnak s mdosthat balrtknek kell lennik, de az eredmny nem balrtk. Az inkrementlsnl (++) a balrtk eggyel nagyobb, dekrementlsnl (--) viszont eggyel kisebb lesz. Eltag opertornl a konstrukci rtke egyezik az j balrtkkel, mg uttag opertornl a konstrukci rtke az inkrementls vagy dekrementls vgrehajtsa eltti rtk. Az eredmny tpust az operandus tpusa hatrozza meg. Pldul:
int x = x = x = x, i = 3, j = 4, n = 5; n++; /* x == 5 s n == 6 */ ++n; /* x == 7 s n == 7 */ --( n - j + i +6); /* x == 11 */

A kifejezs produklhat balrtket, jobbrtket vagy nem szolgltat rtket. A kifejezsnek ezen kvl lehet mellkhatsa is. Pldul a TPUSOK S KONSTANSOK szakaszban megrt strcopy zr sorban
while(cl[i++]=forrs[i])

a hozzrendels mellkhatsaknt az i vgrehajts utni rtke is eggyel nagyobb lesz. A mellkhatst a kifejezs kirtkelse okozza, s akkor kvetkezik be, ha megvltozik egy vltoz rtke. Minden hozzrendels opertornak van mellkhatsa. Lttuk, hogy a balrtkre alkalmazott inkrementlsi s dekrementlsi mveletnek is van. Fggvnyhvsnak is lehet azonban mellkhatsa, ha globlis hatskr objektum rtkt vltoztatja meg. rjuk meg a void chdel(char s[], int c)t, mely sajt helyen trli a benne elfordul c karaktereket az s karakterlncbl! Itt is msolni kell a forrsbl a clba bjtrlbjtra haladva, de a c rtk karaktereket ki kell ebbl hagyni. Kt indexre van szksg. Az egyik az i, mely vgigjrja a forrst. A msik a j, mely a clban mindig a kvetkez szabad helyet ri el. A nem c rtk karaktert a kvetkez szabad helyre kell msolni, s a clbeli indexnek az ezutn kvetkez szabad helyre kell mutatnia.
void chdel(char s[], int c){ int i, j; for(i=j=0; s[i]; ++i) if(s[i] != c) s[j++] = s[i];

92
s[j] = 0; }

MVELETEK S KIFEJEZSEK

Bit szint opertorok ( ~, <<, >>, &, ^ s |) A bit szint opertorok csak signed s unsigned egsz tpus adatokra: char, short, int s long hasznlhatk. Legmagasabb prioritsi szinten az egyoperandusos, jobbrl balra kt egyes komplemens opertor (~) van, melynek defincija:
~ eltag-kifejezs

5.8

Az opertor elbb vgrehajtja az egszellptetst, ha szksges. Az eredmny tpusa az operandus konverzi utni tpusa. Az eredmny maga a bit szint egyes komplemens, azaz ahol az operandus bit 1 volt, ott az eredmny bit 0 lesz, s ahol az operandus bit 0 volt, ott az eredmny bit 1 lesz. Feltve, hogy az egszellptets 16 bites, s hogy:
unsigned short x = 0XF872, maszk = 0XF0F0; /* 1111100001110010 */ /* 1111000011110000 */

, akkor a ~x 00000111100011012, s a ~maszk 00001111000011112. A balrl jobbra csoportost eltols opertorok (<< s >>) prioritsa alacsonyabb az aritmetikai mveleteknl, de magasabb, mint a relci opertorok. Az eltols opertorok els operandusuk rtkt balra (<<) vagy jobbra (>>) toljk annyi bitpozcival, mint amennyit a msodik operandus meghatroz. A definci a kvetkez:
eltols-kifejezs: additv-kifejezs eltols-kifejezs << additv-kifejezs eltols-kifejezs >> additv-kifejezs

A K1<<K2 s a K1>>K2 kifejezsek esetben minkt operandus egsz tpus kell, legyen. Az opertorok egszellptetst is megvalsthatnak. Az eredmny tpust K1 konvertlt tpusa hatrozza meg. Ha K2 negatv vagy rtke nem kisebb K1 bitszlessgnl, akkor az eltolsi mvelet eredmnye hatrozatlan. Miutn a Cben nincs egsz alul vagy tlcsorduls, a mveletek rtkvesztst is okozhatnak, ha az eltolt eredmny nem fr el az els operandus konvertlt tpusban. A K1<<K2 balra tolja K1 rtkt K2 bitpozcival gy, hogy jobbrl 0 bitek jnnek be. K1 tlcsorduls nlkli balra tolsa ekvivalens K1*2K2 vel. Ilyen rtelemben aztn az eltols aritmetikai mveletnek is tekinthet. Ez a gondolatsor igaz persze az sszes bit szint mveletre is! Ha K1 valamilyen signed tpus, akkor az eltols eredmnye csak gonddal szemllhet az eljel bit esetleges kitolsa miatt.

C programnyelv

93

A K1>>K2 mvelet K1 rtkt K2 bitpozcival tolja jobbra. Ha K1 valamilyen unsigned tpus, akkor balrl 0 bitek jnnek be. Ha K1 signed, akkor az opertor az eljel bitet sokszorozza. unsigned, nem negatv K1 esetn a jobbra tols K1/2K2 hnyados egsz rszeknt is interpretlhat. Folytatva az egyes komplemenskpzsnl megkezdett pldt, az x<<2 11100001110010002, ill. a maszk>>5 00000111100001112. A bit szint logikai opertorok prioritsuk cskken sorrendjben az s (&), a kizr vagy (^), valamint a vagy (|). A tbbi mveletre val tekintettel prioritsuk magasabb a ktoperandusos logikai opertoroknl, de alacsonyabb a relciknl. Lssuk a defincit!
s-kifejezs: egyenlsgi-kifejezs s-kifejezs & egyenlsgi-kifejezs kizr-vagy-kifejezs: s-kifejezs kizr-vagy-kifejezs ^ s-kifejezs vagy-kifejezs: kizr-vagy-kifejezs vagy-kifejezs | kizr-vagy-kifejezs

Az egyenlsgi-kifejezs defincija a relciknl megtallhat! Ha szksges, akkor a mvelet elvgzse eltt a fordt implicit tpuskonverzit hajt vgre az egsz tpus operandusok rtkn. Az eredmny tpusa az operandusok konverzi utni tpusa. A mvelet bitrl-bitre valsul meg az operandusok rtkn, s egy bitre vonatkoztatva az eredmny gy nz ki: K1 0 1 0 1 K2 0 0 1 1 K1 & K2 0 0 0 1 K1 ^ K2 0 1 1 0 K1 | K2 0 1 1 1

Befejezve az egyes komplemensnl megkezdett pldt, az x|maszk rtke 11111000111100102. llthatjuk, hogy az eredmnyben minden olyan bit egy, ami a maszkban az volt. Az x^x eredmnye biztosan tiszta zrus. Az x&~maszk rtke 00001000000000102. Megemltjk, hogy az eredmny minden olyan bitpozcijn 0 van, ahol a maszk bitje 1. Teht a kifejezs azokat a biteket bizonyosan trlte, ahol a maszk bit 1 volt.

94

MVELETEK S KIFEJEZSEK

Nem szabad sszekeverni a logikai vagy (||) s a bitenknt vagy (|), ill. a logikai s (&&) s a bit szint s (&) mveleteket! Feltve, hogy kt, egsz tpus vltoz rtke: a=2 s b=4, akkor az
a && b 1 (igaz)

de az
a & b 0

Az ANSI szabvny szerint a bit szint mveletek signed egszeken implementcifggk. A legtbb C fordt azonban signed egszeken ugyangy dolgozik, mint unsignedeken. Pldul short intben gondolkodva a 16 & 99 eredmnye 96, mert:
1111111111110000&0000000001100011 0000000001100000

A fjl utols mdostsnak dtumt s idejt egy-egy szban, azaz C nyelvi fogalmakkal: egy-egy unsigned short int-ben troljuk. A kt sz bitfelosztsa legyen a kvetkez: dtum id v 1980 ra 15 14 13 12 11 10 9 8 hnap perc 7 6 5 4 3 nap 2 msodperc 2 1 0

A kt sz azonostja legyen datum s ido! Ttelezzk fel, hogy az id rszeit az ora, a perc s az mp unsigned short vltozkban troljuk! Az ugyanilyen unsigned short vltozk a dtum rszeire: ev, ho s nap. Akkor az odavisszaalakts a kvetkez:
/* Rszeibl az id sz ellltsa: */ ido = ora << 11 | perc << 5 | mp >> 1; /* Az id szbl a rszei: */ ora = ido >> 11; perc = ido >> 5 & 0X3F; mp = (ido & 0X1F) << 1; /* Rszeibl a dtum sz ellltsa: */ datum = ev - 1980 << 9 | ho << 5 | nap; /* A datum szbl a rszei: */ ev = (datum >> 9) + 1980; ho = datum >> 5 & 0XF; nap = datum & 0X1F;

rjunk unsigned long invertal(unsigned long x, int p, int n) fggvnyt egy rvid kiprbl programmal, mely az x paramtere rtkt a p.ik bitpozcijtl n hosszban invertlja (az 1eseket 0kra, s a 0kat 1esekre cserli)! A nem emltett bitpozcik rtke maradjon vltozatlan! Az invertlt eredmny a fggvny visszatrsi rtke.

C programnyelv n
31 29 27 25 23 21p 19 17 15 13 11 9 7 5 3

95

1 0

Az bra x paramtert, a zrustl indul, 20 rtk p bitpozcit s az n=5 bitszlessget szemllteti. Ksztsnk elbb egy ugyancsak unsigned long maszkot, mely p pozcitl n szlessgben 1es biteket tartalmaz, s az ezen kvli pozcik mind nullk!
~0: ~0<<n: ~(~0<<n): ~(~0<<n)<<(p+1-n): 1111111111111111111111111111111 1111111111111111111111111100000 0000000000000000000000000011111 0000000000011111000000000000000

Ezutn x egyes komplemensbl bitenknti ssel kivgjuk a krdses invertlt bitpozcikat, azaz: ~x&maszk. Ezt aztn bitenknti vagy kapcsolatba hozzuk az eredeti x egy olyan vltozatval, melyben kinullztuk az rdekes biteket, azaz: x&~maszk. Teht:
/* PELDA15.C: Bitpozcik invertlsa. */ #include <stdio.h> unsigned long invertal(unsigned long x, int p, int n){ unsigned long maszk=~(~0<<n)<<(p+1-n); return ~x&maszk|x&~maszk; } void binaris(unsigned long x){ unsigned long maszk; for(maszk=0X80000000; maszk; maszk=maszk>>1) if(maszk&x) putchar(1); else putchar(0); } void main(void){ unsigned long x=0X4C59E9FA; int p=20, n=5; printf("Az eredeti rtket: "); binaris(x); printf("\nEzt invertljuk %d bitpozcitl %d bitszlessgben.\n", p, n); printf("Az invertlt rtk: "); binaris(invertal(x, p, n)); putchar(\n); }

Vegyk szre, hogy a binaris fggvny segtsgvel unsigned long rtket jelentetnk meg binrisan! A maszk vltoz 31es bitpozcijtl indtunk egy 1est, s a ciklusmag vgrehajtsa utn mindig eggyel jobbra toljuk. A maszk s az rtk bit szint s kapcsolata akkor szolgltat nem zrust (igazat), ha a krdses bitpozcin az rtkben 1 van. Megoldand feladatok:

96

MVELETEK S KIFEJEZSEK

Ksztse el a kvetkez fggvnyeket, s prblja is ki ket egy rvid programmal! Az unsigned long getbits(unsigned long x, int p, int n) x rtkt szolgltatja a p.ik bitpozcitl n bitszlessgben. Az unsigned long rotl(unsigned long x) 1 bittel balra forgatja paramtere rtkt. Teht a 31. pozcirl kicsorg bit lesz az eredmny 0. bitje. Az unsigned long rotr(unsigned long x) 1 bittel jobbra forgat. Az unsigned long tobbrotl(unsigned long x, int n) n bittel forgat krbe balra. Az unsigned long tobbrotr(unsigned long x, int n) n bittel forgat krbe jobbra. 5.9 Feltteles kifejezs ( ? :) Ez a nyelvben az egyetlen hrom operandusos mvelet, melynek prioritsa alacsonyabb a ktoperandusos logikai opertoroknl. A definci:
feltteles-kifejezs: logikai-vagy-kifejezs logikai-vagy-kifejezs ? kifejezs : feltteles-kifejezs kifejezs: hozzrendels-kifejezs kifejezs , hozzrendels-kifejezs

A logikai-vagy-kifejezsnek egsz, lebegpontos vagy mutat tpusnak kell lennie, s kirtkelse zrushoz val hasonltst jelenti: Ha logikai-vagy-kifejezs nem zrus, a kifejezst rtkeli ki a fordt. Ez azt jelenti, hogy a kifejezs kirtkelse csak akkor trtnik meg, ha a logikai-vagy-kifejezs igaz. Ha logikai-vagy-kifejezs zrus, a feltteles-kifejezst hatrozza meg a fordt, azaz a feltteles-kifejezs kirtkelse csak akkor trtnik meg, ha a logikai-vagy-kifejezs hamis. A logikai-vagy-kifejezst mindenkppen kirtkeli a kd, de a kifejezs s a feltteles-kifejezs kzl csak az egyik kiszmtsa trtnik meg. A K1 ? K2 : K3 feltteles kifejezsben K1 rtktl fggen K2t vagy K3at rtkeli ki a fordt. A konstrukci eredmnynek tpusa ilyen alapon K2 vagy K3 operandusok tpustl fgg a kvetkezkpp:

C programnyelv

97

Ha mindkett aritmetikai tpus, akkor az esetleg szksges implicit tpuskonverzi utn az eredmny tpusa a konvertlt tpus. Ha a kt operandus ugyanolyan struktra, uni, vagy mutat tpus, akkor az eredmny is a kzs tpus. Ha mindkett void tpus, akkor az eredmny is az. Pldul az
if( a > b ) z = a; else z = b;

utasts helyettesthet a
z = a > b ? a : b;

feltteles kifejezssel. Ha pldul egy int tpus a tmb els N elemt szeretnnk megjelentetni gy, hogy egy sorba egymstl szkzzel elvlasztva 10 elem kerljn, akkor ezt tmr kdot alkalmazva gy is megtehetjk:
for(i=0; i<N; ++i) printf(%6d%c, a[i], (i%10==9|| i==N-1) ? \n: );

Javtsunk ki nhny eddig megrt fggvnyt a feltteles kifejezs felhasznlsval! A PELDA15.C binaris fggvnyben az
if(maszk&x) putchar(1); else putchar(0);

most gy is rhat:
putchar((maszk&x)? 1: 0);

A PELDA14.C atoi rutinjban az


if(s[i]==+||s[i]==-) if(s[i++]==-) elojel=-1;

sor trhat a kvetkezre:


if(s[i]==+||s[i]==-) elojel=(s[i++]==-)? -1: 1;

Mindkt pldban a feltteles kifejezs logikai-vagy-kifejezse kr zrjelet tettnk. Lssuk azonban be, hogy erre semmi szksg sincs, hisz ennl alacsonyabb priorits mvelet mr csak kett van: a hozzrendels s a vessz opertor! A felesleges zrjel legfeljebb a jobban olvashatsgot biztostja. 5.10 Hozzrendels opertorok A jobbrl balra csoportost hozzrendels prioritsa alacsonyabb, mint a feltteles kifejezs, s ennl alacsonyabb priorits mvelet mr csak a

98

MVELETEK S KIFEJEZSEK

vessz opertor. A mvelet a jobb oldali operandus rtkt rendeli a bal oldali operandushoz, melybl kvetkezleg a bal oldali operandusnak mdosthat balrtknek kell lennie. A hozzrendels kifejezs rtke ugyan egyezik a bal oldali operandus hozzrendels vgrehajtsa utni rtkvel, de nem balrtk. A jobb oldali operandus rtkt a fordt a bal oldali operandus tpusra konvertlja a bal operandusba val letrols eltt a hozzrendelsi konverzi szablyai szerint. A bal oldali operandus nem lehet tmb, fggvny vagy konstans. Nem lehet termszetesen nem teljes (mg nem teljesen deklarlt) tpus sem. A definci:
hozzrendels-kifejezs: feltteles-kifejezs egyoperandusos-kifejezs hozzrendels-opertor hozzrendels-kifejzs hozzrendels-opertor: ( a kvetkezk egyike!) = *= /= %= += -= &= ^= |= <<= >>=

Van teht egyszer hozzrendels opertor (=) s vannak sszetettek vagy kombinltak (ezek a tbbiek). Foglalkozzunk elbb az egyszer hozzrendelssel! A K1 = K2 kifejezsben K1-nek mdosthat balrtknek kell lennie. K2 rtke - esetlegesen a K1 tpusra trtnt konverzi utn - fellrja a K1 ltal kijellt objektum rtkt. Az egsz konstrukci rtke K2 rtke az esetleg a K1 tpusra szksgess vlt hozzrendelsi konverzi vgrehajtsa utn. Remljk, hogy nem felejtettk mg el, hogy a balrtk (K1) s jobbrtk (K2) fogalom ppen az egyszer hozzrendelsbl szrmazik. A balrtk a hozzrendels opertor bal oldaln, a jobbrtk pedig a jobb oldaln llhat. Tudjuk, ha egy kifejezsben hozzrendels opertor is van, akkor annak a kifejezsnek bizonyosan van mellkhatsa. Emlkezznk vissza, hogy a definci megengedi a hozzrendels opertor K1 = K2 = K3 = ... = Kn = kifejezs formj hasznlatt is, amikor is a kifejezs kirtkelse utn jobbrl balra haladva az operandusok felveszik a kifejezs rtkt. Az egsz konstrukci rtke most is a kifejezs rtke lesz. Pldul
a = b= c = d + 6;

A kombinlt hozzrendels opertorok a K1 = K1 opertor K2

C programnyelv kifejezst K1 opertor = K2

99

mdon rvidtik, s K1 kirtkelse csak egyszer trtnik meg. A megengedett opertorok a definciban lthatk! Mindegyik megvalstja azt a mveletet, konverzit s korltozst, amit a ktoperandusos opertor egybknt realizl, s vgrehajtja a hozzrendelst is. A kombinlt hozzrendels opertor operandusai egsz vagy lebegpontos tpusak lehetnek ltalban. A += s = bal oldali operandusa mutat is lehet, amikor is a jobb oldali operandus kteles egsz tpus lenni. Pldul:
x = x * ( y + 6); x *= y + 6;

Az sszetett opertorokat hasznlva kevesebbet kell rni. Pldul:


t[ t1[i3 + i4] + t2[i1 - i2]] += 56;

Hasznljunk kombinlt hozzrendels opertorokat nhny eddig mr megrt fggvnyben! A PELDA15.C binaris rutinja:
void binaris(unsigned long x){ unsigned long maszk; for(maszk=0X80000000; maszk; maszk>>=1) putchar((maszk&x)? 1: 0); }

A PELDA4.Cbeli
for(ft=ALSO; ft<=FELSO; ft=ft+LEPES)

most gy rhat:
for(ft=ALSO; ft<=FELSO; ft+=LEPES)

5.11 Hozzrendelsi konverzi Hozzrendelsnl a hozzrendelend rtk tpust a hozzrendelst fogad vltoz tpusra konvertlja a fordt. A C megengedi a hozzrendelsi konverzit lebegpontos s egsz tpusok kztt azzal, hogy a konverzinl rtkveszts trtnhet. A hasznlatos konverzis mdszer kveti az implicit tpuskonverzi szablyait s ezen tl mg a kvetkezket: Konverzi signed egsz tpusokrl: Nem negatv signed egsz nem kisebb mret unsigned egssz alaktsakor az rtk vltozatlan. Pldul signed char unsigned charr vlsakor a bitminta vltozatlan, de a legmagasabb helyirtk bit elveszti az eljelbit funkcijt. A konverzi klnben a signed egsz eljel kiterjesztsvel

100

MVELETEK S KIFEJEZSEK trtnik. Pldul signed char unsigned longg gy vlik, hogy eljel kiterjesztssel elbb long lesz belle, s aztn ez a bitminta megtartsval, s eljelbit funkcivesztssel lesz unsigned long. Hosszabb egsz tpus rvidebb alakulsakor az egsz tpus marad als bitjei vltozatlanok, s a flsleg egyszeren levgdik mg akkor is, ha rtkveszts trtnne. long int rtk float lebegpontoss alaktsakor nincs rtkveszts, de pontossgveszts lehet. Ilyen talaktskor a rvidebb signed egszbl elbb long lesz eljel kiterjesztssel, s csak ezutn jn a lebegpontos konverzi.

Miutn az enum tpus int definci szerint, a felsorols tpusra s tpusrl val konverzi egyezik az intvel. Konverzi unsigned egsz tpusokrl: Rvidebb unsigned vagy signed egssz alaktskor a marad als bitek vltozatlanok, s a flsleg egyszeren levgdik mg akkor is, ha rtkveszts trtnik. Az eredmny legfels bitje felveszi az eljelbit funkcit, ha signed d konvertls volt. Hosszabb unsigned vagy signed egssz alaktskor a bejv magasabb helyirtk bitek nulla feltltsek. Lebegpontos konverzinl a rvidebb unsigned egszbl elbb long lesz nulla feltltssel, s csak ezutn jn az igazi konverzi. Megllapthatjuk itt is, hogy lehet pontossgveszts floatt alaktskor. Konverzi lebegpontos tpusokrl: A rvidebb lebegpontos brzols hosszabb konvertlsakor nem vltozik meg az rtk. A hosszabb lebegpontos brzols floatt alaktsa is pontos, ha csak lehetsges. Pontossgveszts is bekvetkezhet, ha rtkes jegyek vesznek el a mantisszbl. Ha azonban az eredmny a float brzolsi korltain kvl esne, akkor a viselkeds definilatlan. Ez a definilatlansg tulajdonkppen a fordttl fgg viselkedst takar! A lebegpontos rtket gy konvertl egssz a fordt, hogy levgja a trtrszt. Az eredmny elre megjsolhatatlan, ha a lebegpontos rtk nem fr be az egsz brzolsi korltaiba. Klnsen definilatlan a negatv lebegpontos rtk unsignedd alaktsa. Konverzi ms tpusokrl: Nincs konverzi a struktra s az uni tpusok kztt. Explicit tpusmdostssal brmilyen rtk konvertlhat void tpusv, de csak abban az rtelemben, hogy a kifejezs rtkt elvetjk. A void tpusnak definci szerint nincs rtke. Ebbl kvetkezleg nem konvertlhat ms tpusra, s ms tpus sem konvertlhat voidra hozzrendelssel.

C programnyelv 5.12 Vessz opertor Ez a legalacsonyabb priorits mvelet.


kifejezs: hozzrendels-kifejezs kifejezs , hozzrendels-kifejezs

101

A K1, K2, ..., Kn esetn balrl jobbra haladva kirtkeli a fordt a kifejezseket gy, hogy a bennk foglalt minden mellkhats is megvalsul. Az els n - 1 kifejezs voidnak tekinthet, mert az egsz konstrukci tpust s rtkt a legjobboldalibb kifejezs tpusa s rtke hatrozza meg. Pldul a
regikar = kar, kar = getchar()

esetn regikar felveszi kar pillanatnyi rtkt, aztn kar s az egsz kifejezs rtke a szabvny bemenetrl beolvasott karakter lesz. Tipikus plda mg a tbb kezdrtk ads s lptets a for utastsban: rjuk meg egy rvid kiprbl programmal a void strrv(char s[]) fggvnyt, mely sajt helyn megfordtja a paramter karakterlncot! Az algoritmusrl annyit, hogy a karakterlnc els karaktert meg kell cserlni az utolsval, a msodikat az utols elttivel, s gy tovbb. Kt indexet kell indtani a karaktertmbben: egyet alulrl s egyet fellrl. Az alst minden csere utn meg kell nvelni eggyel, a felst pedig ugyanenynyivel kell cskkenteni. A ciklus teht addig mehet, mg az als index kisebb a felsnl.
/* PELDA16.C: Karakterlnc megfordtsa. */ #include <stdio.h> #include <string.h> /* Az strlen miatt! */ #define INP 66 /* Az input puffer mrete. */ void strrv(char s[]){ int also, felso, csere; for(also=0, felso=strlen(s)-1; also<felso; ++also, --felso){ csere=s[also]; s[also]=s[felso]; s[felso]=csere; } } int getline(char s[],int n){ int c,i; for(i=0;i<n&&(c=getchar())!=EOF&&c!='\n';++i) s[i]=c; s[i]='\0'; while(c!=EOF&&c!='\n') c=getchar(); return(i); } void main(void){ char s[INP+1]; /* Az input puffer. */ printf("A szabvny bemenet sorainak megfordtsa.\n" "Programvg: res sor.\n\n"); while(printf("Jhet a sor! "), getline(s,INP)){ strrv(s);

102

MVELETEK S KIFEJEZSEK
printf("Megfordtva: %s\n", s); } }

A STRING.H fejfjlt bekapcsolva rendelkezsre ll az strrv szabvny knyvtri vltozata, mely ugyanilyen paramterezs s funkcij, de strrev a neve, s ms egy kicsit a visszatrsi rtknek a tpusa. Vegyk szre, hogy a mainbeli whileban is vesszs kifejezst hasznltunk! Olyan szvegkrnyezetben, ahol a vessz szintaktikai jelents, a veszsz opertort csak zrjelbe tett csoporton bell szabad hasznlni. Ilyen helyek: az inicializtorlista pldul, vagy a fggvny paramterlistja. A
fv(b, (a = 2, t += 3), 4);

kitnen mutatja, hogy az fv fggvny hrom paramterrel rendelkezik. A hvsban a msodik paramter vesszs kifejezs, ahol a elbb 2 rtk lesz, aztn t megn hrommal, s ezt az rtket kapja meg a fggvny is msodik aktulis paramterknt. Ha a fggvny prototpusa mst nem mond, akkor a msodik paramter tpusa t tpusa. 5.13 Mveletek prioritsa A mveletek prioritst nevezik precedencinak, vagy rendsgnek is. Mindhrom esetben arrl van sz, hogy zrjel nlkli helyzetben melyik mveletet kell vgrehajtani elbb a kifejezs kirtkelse sorn. A kvetkez tblzat cskken prioritssal haladva mutatja az egyes opertorok asszociativitst. A tbbszr is elfordul opertorok kzl mindig az egyoperandusos a magasabb priorits. Abban a rovatban, ahol tbb opertor van egytt, a mveletek azonos prioritsak, s asszociativitsuknak megfelelen hajtja ket vgre a fordt. Minden opertor kategrinak megvan a maga asszociativitsa (balrl jobbra vagy jobbrl balra kt), mely meghatrozza zrjel nlkli helyzetben a kifejezs csoportostst azonos priorits mveletek esetn.

C programnyelv Opertorok () [] -> . ! ~ + - ++ -- & * sizeof */% +<< >> < <= > >= == != & ^ | && || ?: = *= /= %= += -= &= ^= |= <<= >>= , Asszociativits balrl jobbra jobbrl balra balrl jobbra balrl jobbra balrl jobbra balrl jobbra balrl jobbra balrl jobbra balrl jobbra balrl jobbra balrl jobbra balrl jobbra jobbrl balra jobbrl balra balrl jobbra

103

13. bra: Mveletek prioritsa

A tblzatban felsoroltakon kvl van mg a # s a ## opertor, melyek az elfeldolgoznak szlnak. A prioritsi tblzat (13. bra) termszetesen tartalmaz eddig mg nem ismertetett mveleteket is. Vannak tbbjelents opertorok is, melyek rtelmezse a helyzettl fgg. Pldul:
cimke: a?x:y a=(b+c)*d; void fv(int n); a, b, c; fv(a, b, c); /* /* /* /* /* /* utasts cmke */ feltteles kifejezs */ zrjeles kifejezs */ fggvnydeklarci */ vesszs kifejezs */ fggvnyhvs */

A kifejezs kirtkelsi sorrendje nem meghatrozott ott, ahol a nyelvi szintaktika errl nem gondoskodik. A fordt a generlt kd hatkonysgnak javtsa rdekben trendezheti a kifejezst klnsen az asszocia-

104

MVELETEK S KIFEJEZSEK

tv s kommutatv opertorok (*, +, &, ^ s |) esetn, hisz azt felttelezi, hogy a kirtkels irnya nem hat a kifejezs rtkre. Fedezzk fel, hogy itt nem arrl van sz, hogy a fordt a kvetkez fordtskor msknt rendezi t a kifejezst, hisz a fordts determinisztikus dolog, hanem arrl, hogy klnbz C fordtk msms eredmnyre juthatnak! Problma lehet az olyan kifejezssel, melyben ugyanazt az objektumot egynl tbbszr mdostjuk, ill. melyben ugyanazon objektum rtkt vltoztatjuk s felhasznljuk. Pldul:
i = v[i++]; /* Dntsk el, mit is akarunk i-vel! */ i = a+++b[a]; /* b indexe attl fgg, hogy az sszeads melyik tagjt rtkeli ki elbb a fordt. */

A fentiek akkor is igazak, ha kifejezsnket jl sszezrjelezzk:


int osszeg=0; sum = (osszeg=3)+(++osszeg);/* sum == 4 vagy 7 ? */

Segdvltozk bevezetsvel a dolgok midig egyrtelmsthetk:


int seged, osszeg=0; seged = ++osszeg; sum = (osszeg=3)+seged; /* seged == 1, osszeg == 1. */ /* sum == 4. */

Ha a szintaktika rgzti a kirtkelsi sorrendet (az &&, a ||, a ?: s a vessz opertor esetben ez gy van), akkor ott brmit megtehetnk. Pldul:
sum = (i=3, i++, i++); /* OK, sum == 4 s i == 5. */

A kifejezs kirtkelsi sorrendjt ugyan () prokkal befolysolhatjuk:


f = a*(b+c);

, de asszociatv s kommutatv opertorok operandusait mg agyonzrjelezve is sszecserlheti a fordt, hisz felttelezheti, hogy a kifejezs rtkt ez nem befolysolja:
f = (a+b) + (c+d);

Hatrozatlan a fggvny aktulis paramtereinek kirtkelsi sorrendje is:


printf(%d %d\n, ++n, fv(n));

A bitenknti opertorok prioritsa alacsonyabb a relciknl, gy a


c & 0XF == 8

C programnyelv

105

mindig hamis, mert az elbb kirtkelsre kerl egyenlsgi relci sohasem lehet igaz. A kifejezs helyesen:
(c & 0XF) == 8.

Vigyzzunk a hozzrendels (=) s az egyenl relci (==) opertor felcserlsre, mert pldul az
if( i = 2 ) utasts1; else utasts2;

else gra sohasem jut el a vezrls tekintettel arra, hogy a 2 hozzrendelse ebben az letben sem vlik hamiss. gyeljnk azokkal a kifejezsekkel is, melyekben csak logikai s (&&) vagy csak logikai vagy (||) mveletek vannak, mert ezeket balrl jobbra haladva && esetn csak az els hamis tagig, ill. || opertornl csak az els igaz tagig fogja kirtkelni a fordt! Az
x && y++

kifejezsben y nvelse csak akkor trtnik meg, ha x nem zrus. Kifejezs kirtkelse kzben addhatnak thidalhatatlan szitucik. Ilyenek a zrussal val oszts s a lebegpontos tl vagy alulcsorduls. jra felhvjuk azonban a figyelmet arra, hogy a nyelvben nem ltezik sem egsz tl, sem alulcsorduls!

106

UTASTSOK

6 UTASTSOK
Az utastsokat ha mst nem mondanak megadsuk sorrendjben hajtja vgre a processzor. Az utastsoknak nincs rtke. Vgrehajtsuk hatssal van bizonyos adatokra, vagy programelgazst valstanak meg stb. Defincijuk a kvetkez:
utasts: sszetett-utasts cmkzett-utasts kifejezs-utasts szelekcis-utasts itercis-utasts ugr-utasts

6.1

sszetett utasts

sszetett-utasts: { <deklarcilista><utastslista> } deklarcilista: deklarci deklarcilista deklarci utastslista: utasts utastslista utasts

Az sszetett utasts utastsok (lehet, hogy res) listja { }-be tve. Az sszetett utastst blokknak is nevezik, mely szintaktikailag egy utastsnak minsl, s szerepet jtszik az azonostk hatskrben s lthatsgban. Ha a deklarcilistban elfordul azonostt mr korbban az szszetett utastson kvl is deklarltk, akkor a blokkra loklis azonost elfedi a blokkon kvlit a blokk teljes hatskrben. Teht az ilyen blokkon kvli azonost a blokkban nem lthat. C blokkban elbb a deklarcis, s csak aztn a vgrehajthat utastsok kvetkeznek. Az sszetett utastsok akrmilyen mly szinten egymsba gyazhatk, s az elbbi szerkezet a begyazott blokkra is vonatkozik. Tudjuk, hogy az auto trolsi osztly objektumok inicializlsa mindannyiszor megtrtnik, valahnyszor a vezrls a fejen t kerl be az sszetett utastsba. Ez az inicializls azonban elmarad, ha a vezrls ugr utastssal rkezik a blokk kzepre. Tilos ;t rni az sszetett utasts zr }e utn!

C programnyelv 6.2 Cmkzett utasts

107

cmkzett-utasts: azonost : utasts case konstans-kifejezs : utasts default : utasts

A case s a default formk csak switch utastsban hasznlatosak. Ezek ismertetsvel majd ott foglalkozunk! Az
azonost : utasts

alakban az azonost (cmke) clja lehet pldul egy felttlen elgaztat


goto azonost;

utastsnak. A cmkk hatskre mindig az ket tartalmaz fggvny. Nem deklarlhatk jra, de kln nvterletk van, s nmagukban nem mdostjk az utastsok vgrehajtsi sorrendjt. C-ben csak vgrehajthat utasts cmkzhet meg, azaz
{ /* . . . cimke: } /* HIBS */

A megolds helyess vlik, ha legalbb egy res utastst tesznk a cimke utn:
{ /* . . . cimke: ; } /* OK */

6.3

Kifejezs utasts

kifejezs-utasts: <kifejezs>;

Ha a kifejezst pontosvessz kveti, kifejezs utastsnak minsl. A fordt kirtkeli a kifejezst, s ekzben minden mellkhats rvnyre jut, mieltt a kvetkez utasts vgrehajtsa elkezddne. Pldul az
x = 0, i++, printf(Hah!\n)

kifejezsek, s gy vlnak kifejezs-utastsokk:


x = 0; i++; printf(Hah!\n);

A legtbb kifejezs utasts hozzrendels vagy fggvnyhvs. res utastst (null statement) gy kapunk, hogy kifejezs nlkl pontosvesszt tesznk. Hatsra termszetesen nem trtnik semmi, de ez is

108

UTASTSOK

utastsnak minsl, azaz szintaktikailag llhat ott, ahol egybknt utasts llhat. 6.4 Szelekcis utastsok
szelekcis-utasts: if(kifejezs) utasts if(kifejezs) utasts else utasts switch(kifejezs) utasts

Ltszik, hogy kt szelekcis utasts van: az if s a switch. A felttelesen elgaztat


if(kifejezs) utasts1 else utasts2

utastsban a kifejezsnek aritmetikai, vagy mutat tpusnak kell lennie. Ha rtke zrus, akkor a logikai kifejezs hamis, msklnben viszont igaz. Ha a kifejezs igaz, utasts1 kvetkezik. Ha hamis, s az else g ltezik, akkor utasts2 jn. Mind utasts1, mind utasts2 lehet sszetett utasts is! A nyelvben nincs logikai adattpus, de cserben minden egsz s mutat tpus annak minsl. Pldul:
if(ptr == 0) if(ptr != 0) /* Rvidthet if(!ptr)-nek. */ /* Rvidthet if(ptr)-nek. */

Az else g elhagyhatsga nha problmkhoz vezethet. Pldul a


if( x == 1) if( y == 1 ) puts( x = 1 s y = 1\n); else puts( x != 1\n);

forrsszvegbl a programoz elkpzelse teljesen vilgos. Sajnos azonban egyet elfelejtett. Az else mindig az ugyanazon blokk szinten lev, forrsszvegben megelz, else g nlkli if-hez tartozik. A megolds helyesen:
if( x == 1) { if( y == 1 ) puts( x = 1 s y = 1\n); } else puts( x != 1\n);

Az if utastsok tetszleges mlysgben egymsba gyazhatk, azaz mind az if, mind az else utastsa lehet jabb if utasts is. Pldul:
if( x == 1) { if( y == 1 ) puts( x = 1 s y = 1\n); else puts( x = 1 s y != 1\n);} else { if( y == 1 ) puts( x != 1 s y = 1\n);

C programnyelv
else puts( x != 1 s y != 1\n);}

109

Tbbirny elgazst (szelekcit) valst meg a kvetkez konstrukci:


if( kifejezs1 ) utasts1 else if( kifejezs2 ) utasts2 else if ( kifejezs3 ) utasts3 /* . . . */ else utastsN

Ha valamelyik if kifejezse igaz, akkor az azonos sorszm utastst hajtja vgre a processzor, majd a konstrukcit kvet utasts jn. Ha egyik if kifejezse sem igaz, akkor viszont utastsN kvetkezik. Ha egy n elem, nvekvleg rendezett t tmbben keresnk egy x rtket, akkor ezt binris keressi algoritmust alkalmazva gy interpretlhatjuk:
int binker(int x, int t[], int n){ int also=0, felso=n-1, kozep; while(also<=felso){ kozep=(also+felso)/2; if(x<t[kozep]) felso=kozep-1; else if(x>t[kozep]) also=kozep+1; else return kozep; } /* Megvan. */ return (-1); } /* Nincs meg. */

A binker hromirny elgazst realizl. x t tmbbeli elfordulsnak indext szolgltatja, ill. 1et, ha nincs is x rtk elem a tmbben. A binris keress nvekvleg rendezett tmbben gy trtnik, hogy elszr megllaptjuk, hogy a keresett rtk a tmb als, vagy fels felbe esik. A mdszert aztn jraalkalmazzuk az aktulis flre, s gy tovbb. A dolognak akkor van vge, ha a keres tartomny semmiv szkl (ilyenkor nincs meg a keresett rtk), vagy a felez tmbelem egyezik a keresett rtkkel. Rendezett sorozatban egy rtk keressnek ez a mdja, s nem a minden elemhez trtn hasonltgats! Megoldand feladatok: Ksztsen olyan binker fggvnyt, mely: cskkenleg rendezett tmbben keres! ptllagos paramterben kapja meg, hogy cskken vagy nvekv a rendezettsg!

110

UTASTSOK

plusz paramter nlkl is eldnti, hogy cskkenleg, vagy nvekvleg keressen a tmbben! A tbbirny programelgaztats msik eszkze a
switch(kifejezs) utasts

, melyben a kifejezsnek egsz tpusnak kell lennie. Az utasts egy olyan specilis sszetett utasts, mely tbb case cmkt
case konstans-kifejezs : utasts

s egy elhagyhat default cmkt


default : utasts

tartalmazhat. A vezrlst azon case cmkt kvet utasts kapja meg, mely konstans-kifejezsnek rtke egyezik a switchbeli kifejezs rtkvel. A vgrehajts aztn itt addig folytatdik, mg break utasts nem kvetkezik, vagy vge nincs a switch blokkjnak. A case cmkebeli konstans-kifejezsnek is egsz tpusnak kell lennie. Ez a TPUSOK S KONSTANSOK szakasz Deklarci fejezetben rottakon tl tovbbi korltozsokat r a konstans kifejezsre. Operandusai csak egsz, felsorols, karakteres s lebegpontos llandk lehetnek, de a lebegpontos konstanst explicit tpuskonverzival egssz kell alaktani. Operandus lehet mg a sizeof opertor is, aminek operandusra termszetesen nincsenek ilyen korltozsok. A vgrehajts sorn a kifejezs s a case konstans-kifejezsek rtkn is vgbemegy az egszellptets. A kifejezs az sszes esetleges mellkhatsval egyetemben valsul meg, mieltt az rtkhasonlts megkezddne. Ha nincs a switch kifejezs rtkvel egyez case cmke, akkor a vezrlst a default cmke utastsa kapja meg. Ha nincs default cmke sem, akkor vge van a switchnek. Az utasts hasznlathoz mg kt megjegyzst kell fzni: Tbb case cmke is cmkzhet egy utastst. Egyazon switch utastsban viszont nem fordulhat el kt azonos rtk case konstans-kifejezs. A switch utastsok egymsba is gyazhatk, s ilyenkor a case s a default cmkk mindig az ket kzvetlenl tartalmaz switchhez tartoznak. A switch utastst szemlltetend rjuk t a szabvny bemenet karaktereit kategrinknt leszmll PELDA7.Ct!

C programnyelv
/* PELDA17.C: A bemenet karaktereinek leszmllsa kategrinknt */ #include <stdio.h> void main(void){ short k, num=0, feher=0, egyeb=0; printf("Bemeneti karakterek leszmllsa\n" "kategrinknt EOF-ig, vagy Ctrl+Z-ig.\n"); while((k=getchar())!=EOF) switch(k){ case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': ++num; break; case ' ': case '\n': case '\t': ++feher; break; default: ++egyeb; } printf("Karakter szmok:\n----------------\n" "numerikus: %5hd\nfehr: %5hd\n" "egyb: %5hd\n----------------\n" "ssz: %10ld\n", num, feher, egyeb, (long)num+feher+egyeb); }

111

Belthat, hogy a switch utasts hasznlhatsgt szegnyti, hogy a kifejezs csak egsz tpus lehet s, hogy a case cmkk csak egszrtk konstans-kifejezsek lehetnek. Az ifnl emltett tbbirny elgaztatsi konstrukci gy jval ltalnosabban hasznlhat. 6.5 Itercis utastsok
itercis-utasts: while(kifejezs) utasts do utasts while(kifejezs); for(<kifejezs>; <kifejezs>; <kifejezs>) utasts

Szemmel lthatan hromfle itercis utasts van, amibl kett elltesztel ciklusutasts. A
while(kifejezs) utasts

112

UTASTSOK

kifejezsrl ugyanaz mondhat el, mint az if utasts kifejezsrl. Lpsenknt a kvetkez trtnik: 1. Kirtkeli a fordt a kifejezst, melynek sorn minden esetleges mellkhats is megvalsul. Ha hamis (zrus) az rtke, akkor vge a ciklusnak, s a while-t kvet utasts jn a programban. 2. Ha a kifejezs igaz (nem zrus), akkor az utasts vgrehajtsa, s jbl az 1. pont kvetkezik. Vilgos, hogy a kifejezs rtknek valahogyan vltoznia kell az utastsban, klnben a ciklusnak soha sincs vge. Az utasts llhat tbb utastsbl is, azaz sszetett utasts is lehet. Ha az utastsok kzt ugr utasts is van, akkor ezen md is kilphetnk a ciklusbl. A
for(<init-kifejezs>; <kifejezs>; <lptet-kifejezs>) utasts

elltesztel, iteratv ciklusutasts, melynek megvalsulsa a kvetkez lpsekbl ll: 1. A fordt vgrehajtja az init-kifejezst, ha van. Tbbnyire egy vagy tbb vltoz rtkadsrl lehet itt sz. 2. Kirtkeli a kifejezst. Ha hamis (zrus), akkor vge a ciklusnak, s a for-t kvet utasts jn a programban. Lthat, hogy a szintaktika szerint ez a kifejezs is elhagyhat. Ilyenkor 1 (igaz) kerl a helyre, azaz a vgtelen ciklus knnyedn felrhat:
for( ; ; ); for( ; 1; );

3. Ha a kifejezs igaz (nem zrus), akkor az utasts jn, 4. aztn a lptet-kifejezs, majd jbl a 2. pont kvetkezik. Ha a for utastst while-lal szeretnnk felrni, akkor azt gy tehetjk meg, ha a ciklusmagban nincs continue: <init-kifejezs>; while(<kifejezs>) { utasts; <lptet-kifejezs>; } A szintaktikai szablyt sszefoglalva: a for-bl akr mindegyik kifejezs is elhagyhat, de az els kettt zr pontosvesszk nem! A vessz opertor alkalmazsval mind az init-, mind a lptetkifejezs tbb kifejezs is lehet. Ezt szemllteti az elz szakaszban a PELDA16.Cben megrt strrv fggvny.

C programnyelv

113

rjunk void rendez(int a[], int n) fggvnyt, mely nvekvleg rendezi sajt helyn az n elem a tmbt! Indulva az els elemtl megkeressk a tmb minimlis elemt, s kicserljk az els elemmel. A 2. elemtl kezdve a htra lev tmbrszben keressk meg a legkisebb elemet, s ezt kicserljk a 2. elemmel, s gy tovbb. Ltszik, hogy minimumkeresst utoljra a tmb utols eltti s utols elemn kell elvgezni, s az itteni esetleges csere utn rendezett is az egsz tmb.
void rendez(int a[],int n){ int i, j, m, cs; for(i=0; i<n-1; ++i){ for(j=i+1, m=i; j<n; ++j) if(a[j]<a[m]) m=j; if(i!=m){ cs=a[i]; a[i]=a[m]; a[m]=cs;} } }

Ez ugyebr plda az egymsba gyazott ciklusokra! Ksztsk el az int egesze(char s[]) rutint, mely ellenrzi a decimlis egsz konstans rsszablyt a paramter karaktertmbn. 1et szolgltat, ha a dolog rendben van, s zrust, ha nem!
/* A decimlis szmjegyek szma maximlisan: */ #define HSZ sizeof(int)/sizeof(short)*5 int egesze(char s[]){ int i = 0, kezd; /* A karakterlnc eleji fehr karakterek tlpse: */ while(s[i]==' ' || s[i]=='\n' || s[i]=='\t') ++i; /* Az eljel tlpse: */ if(s[i]=='+' || s[i]=='-') ++i; kezd=i; /* A szmjegyek itt kezddnek. */ /* Elre a kvetkez nem numerikus karakterre: */ while(s[i]>='0' && s[i]<='9' && i-kezd<HSZ) ++i; /* Dnts: */ if(kezd==i || s[i]!='\n' && s[i]!='\t' && s[i]!=' ' && s[i]!=0) return 0; else return 1; }

Ltszik, hogy HSZ 16 bites int esetn 5 (32767), s 32 bitesnl 10 (2147483647). Arra val, hogy ennl tbb decimlis szmjegyet ne fogadjon el a rutin. Vegyk szre, hogy a fggvny visszautastja, ha egyetlen szmjegy karaktert sem tartalmaz a numerikus karakterlnc! Elveti azt is, ha a numerikus lnc rsz nem fehr karakterrel, vagy lnczr zrussal vgzdik. Az ellenrzs persze nem tkletes, hisz a numerikus karakterlnc brzolsi hatrok kz frst nem biztostja. Pldul 16 bites intnl minden tje-

114

UTASTSOK

gy szmot rvnyesnek tekint, holott a 99999 s 32769, valamint a 32768 s 99999 kztti szmok brzolhatatlanok. Ez az ellenrzs azonban csak akkor lenne knnyen megvalsthat, ha tesztels kzben konvertlnnk is a szmot. Ekkor azonban mr tbbe kerlne a leves, mint a hs. Ksztsk programot, mely egsz szmokat kr be. Meghatrozza az tlagukat, a minimumukat, maximumukat, s nvekvleg rendezi is ket! Az egszek darabszma csak futs kzben dl el.
/* PELDA18.C: Egsz szmok tlaga, minimuma, maximuma s rendezse */ #include <stdio.h> #include <stdlib.h> /* Az atoi miatt! */ #include <limits.h> /* INT_MIN s INT_MAX vgett! */ #define MAX 100 /* A tmb max. elemszma. */ #define INP 20 /* Az input puffer mrete. */ int getline(char s[],int lim); /* A decimlis szmjegyek szma maximlisan: */ #define HSZ sizeof(int)/sizeof(short)*5 int egesze(char s[]); void rendez(int a[],int n); void main(void){ int n=0; /* A rendezend elemek szma. */ char sor[INP+1]; /* Az input puffer. */ int a[MAX]; /* A egszeket trol tmb. */ int min, max; /* Minimum, maximum. */ double osszeg=0.;/* Az sszeg */ int i; while(n<1||n>MAX){ printf("\nHny egsz szmot rendeznk(1-%d)? ",MAX); getline(sor,INP); if(egesze(sor)) n=atoi(sor);} printf("\n\nKrem a rendezend szmokat %d s +%d kztt!\n", INT_MIN, INT_MAX); for(i=0;i<n;++i){ printf("%3d: ",i+1); if(getline(sor,INP)>0 && egesze(sor)) a[i]=atoi(sor); else --i; } min=max=a[0]; for(i=0;i<n;osszeg+=a[i],++i) if(a[i]<min) min=a[i]; else if(a[i]>max) max=a[i]; printf( "\nA szmsorozat\tminimuma:%14d.\n" "\t\tmaximuma:%14d.\n" "\t\ttlaga: %17.2f\n", min, max, osszeg/n); printf("\nA rendezett szmok:\n"); rendez(a, n);

C programnyelv
for(i=0; i<n; i++){ printf("%13d",a[i]); if(!((i+1)%6)) putchar('\n'); } putchar('\n'); }

115

Nem rendezett sorozatban a minimum, vagy maximum megkeressnek az a mdja, hogy vesszk a sorozat egy ltez elemt (tbbnyire az elst), s aztn hasonltgatjuk a tbbiekhez, hogy vajon vane nlnl kisebb, ill. nagyobb. Ha van, akkor attl kezdve azzal folytatjuk a hasonltst. El kell ismerni termszetesen, hogy a minimum s maximum megkeresse teljesen felesleges volt, mert a rendezs utn ezeket az rtkeket a[0] s a[n1] amgy is tartalmazza. Vegyk mg szre, hogy az egszek sszegt a for ciklus lptetkifejezsben szmtjuk ki! Megoldand feladatok: Alaktsa t a PELDA18.Cben megvalstott programot a kvetkezkpp: Ne krje be elre a rendezend szmok darabszmt, hanem az rtk bekrsekor jelentse res sor a bemenet vgt! Dolgozzk nem egsz, hanem vals szmokkal a program! A
do utasts while(kifejezs);

htultesztel ciklusutasts. A kifejezsre ugyanazon megktsek rvnyesek, mint a whilenl. A dolog lnyege az, hogy fordt a kifejezs rtktl fggetlenl egyszer biztosan 1. vgrehajtja az utastst. 2. Kirtkeli a kifejezst. Ha hamis (zrus), akkor vge a ciklusnak, s a while-t kvet utasts jn a programban. Ha viszont igaz, akkor az 1. pont kvetkezik. sszestve: Az utastst egyszer mindenkpp vgrehajtja a proceszszor, s ezt kveten mindaddig ismtli, mg a kifejezs hamis nem lesz. A szemlltet pldban az itoa fggvny az int n paramtert karakterlncc konvertlja, s elhelyezi a paramter s karaktertmbben:
#include <stdio.h> #include <string.h> #include <limits.h>

116
void itoa(int n, char s[]){ int i=0, elojel, j=0; if(n==INT_MIN) { ++n; ++j; } if((elojel=n)<0) n=-n; do s[i++]=n%10+0; while(n/=10); if(elojel<0) s[i++]=-; s[i]=0; s[0]+=j; strrev(s); }

UTASTSOK

A belsbrzolsi formbl karakterlncc alakt itoa rutinban az i ciklusvltoz. A j logikai vltoz, s indul rtke 0. Egyetlen egy esetben egyrtk, ha n ppen INT_MIN. A problma az ugye, hogy mindenkpp pozitv rtket kvnunk konvertlni az esetleges negatv eljelet megjegyezve, de az INT_MIN 1szerese nem brzolhat intknt, hisz ppen eggyel nagyobb INT_MAXnl. Ha ilyenkor megnveljk eggyel n rtkt, akkor 1szerese pp a fels brzolsi korlt lesz, amivel mr nincs problma. A j logikai vltoz teht akkor 1, ha volt nvels. Az elojel vltozra n eredeti eljelnek megtartsa vgett van szksg, s azrt, hogy negatv n esetn a keletkez karakterlnc vgre ki lehessen tenni a mnuszjelet. Az algoritmus ppen megfordtva lltja el a karakterlncot. Nzzk csak asztali teszttel a 16 bites int esett! Tegyk fel, hogy n rtke az ominzus 32768! Amig a dowhile utastsig nem rnk, j 1 lesz, n 32767 s i zrus marad. Lejtszva a ciklust a kvetkez trtnik: j: n: i: s: 1 32767 0 7 3276 1 6 327 2 7 32 3 2 3 4 3 0 5 0 6 \0

A tblzatban az az llapot ltszik, amikor a dowhilet kvet kt utasts is lezajlott. Az s[0]bl, vagyis 7bl, 8 lesz, s aztn a szabvny knyvtri strrev megfordtja a sajt helyn az eredmny karakterlncot. 6.6 Ugr utastsok
ugr-utasts: break; continue; goto azonost; return <kifejezs>;

C programnyelv

117

Csak itercis (while, do-while s for) vagy switch utastson bell hasznlhat a
break;

, mely befejezi ezeket az utastsokat, azaz hatsra a vezrls felttel nlkl kilp bellk. Tbb egymsba gyazott itercis utasts esetn a break csak abbl a ciklusbl lptet ki, amelyben elhelyeztk, azaz a break csak egyszint kilptetsre kpes. Ksztend egy int trim(char s[]) fggvny, mely a paramter karaktertmb elejrl s vgrl eltvoltja a fehr karaktereket a sajt helyn, s visszatr az gy letiszttott karakterlnc hosszval!
#include <string.h> int trim(char s[]){ int i=0, n; /* A fehr karakterek eltvoltsa a lnc vgrl: */ for(n=strlen(s)-1; n>=0; --n) if(s[n]!= &&s[n]!=\n&&s[n]!=\t) break; s[++n]='\0'; /* A fehr karakterek eltvoltsa a lnc elejrl: */ while(s[i]== ||s[i]==\n||s[i]==\t) ++i; if(i) { n=0; while(s[n++]=s[i++]); --n;} return(n); }

Csak itercis utastsokban (while, do-while s for) alkalmazhat a


continue;

, mely a vezrlst a kifejezs kirtkelsre viszi while s do-while estn, ill. hatsra a lptet-kifejezs kirtkelse kvetkezik for utastsnl. Egymsba gyazott itercis utastsok esetn ez is mindig csak az t magba foglal itercis utastsra vonatkozik. Ugyebr switchben csak itercis utastson bell hasznlhat a continue! Felttlen vezrlstadst hajt vgre az azonostval cmkzett utastsra a
goto azonost;

Az utastst brmilyen mly blokk szintrl is vgrehajtja a processzor, de a cl cmkzett utastsnak ugyanabban a fggvnyben kell lennie, ahol a goto is van. A void visszatrsektl eltekintve a fggvny testben lennie kell legalbb egy
return <kifejezs>;

118

UTASTSOK

utastsnak. Ha a rutin visszatrsi tpusa tpus, akkor a kifejezs tpusnak is ennek kell lennie, vagy hozzrendelsi konverzival ilyen tpusv alaktja a kifejezs rtkt a fordt. A return hatsra visszakapja a vezrlst a hv fggvny, s tveszi a visszatrsi rtket is. A tpus visszatrs fggvnyt hv kifejezs
fv(aktulis-paramterlista)

tpus tpus jobbrtk, s nem balrtk:


tpus t; t=fv(aktulis-paramterlista); t=++fv(aktulis-paramterlista); /* OK */ /* OK */

A fggvnyhvs hatsra beindult vgrehajts a return utasts bekvetkeztekor befejezdik. Ha nincs return, akkor a fggvny testt zr }-ig megy a vgrehajts. Ha a fggvny ltal visszaadott rtk tpusa void, s a fggvnyt nem a testt zr }-nl szeretnnk befejezni, akkor a fggvny belsejbe a kvnt helyre return utastst kell rni.

C programnyelv

119

7 ELFELDOLGOZ (PREPROCESSOR)
A fordt els menete sorn mindig meghvja az elfeldolgozt a forrsfjlra. Ha szeretnnk tudni, hogy fest az elfeldolgozson tesett forrsfjl (a fordtsi egysg), akkor a programfejleszt rendszerben utna kell nzni, hogy tehetjk lthatv az elfeldolgozs eredmnyt. Az elfeldolgozott forrsfjlban aztn megtekinthetjk: a makrk kifejtst, a behozott (include) fjlokat, a feltteles fordtst, a szomszdos karakterlnc konstansok egyestst, a direktvk elhagyst s a megjegyzsek kimaradst (helyettestst egyetlen szkz karakterrel). A nem karakter, vagy karakterlnc konstansban elfordul, egymst kvet, tbb fehr karaktert mindig eggyel helyettesti az elfeldolgoz. Az elfeldolgoz direktvk rsszablya, mely fggetlen a C nyelv tbbi rsztl, a kvetkez: A sor els, nem fehr karakternek #-nek kell lennie. A #-et kvetheti aztn fehr karakter a soremelst kivve. A sorokra trdels nagyon lnyeges elem, mert az elfeldolgoz sorokra bontva elemzi a forrsszveget. A karakter konstansban, a karakterlnc konstansban s a megjegyzsben lev # karakter nem minsl elfeldolgoz direktva kezdetnek. A direktvkat - miutn nem C utastsok - tilos pontosvesszvel lezrni! Ha a direktvban a soremelst \ karakter elzi meg, akkor a kvetkez sor folytatssornak minsl, azaz az elfeldolgoz elhagyja a \t s a soremelst, s egyesti a kt sort. Az elfeldolgoz direktvkba megjegyzs is rhat.

120

ELFELDOLGOZ DIREKTVK

Az elfeldolgoz direktvk brhol elhelyezkedhetnek a forrsfjlban, de csak a forrsfjl ezt kvet rszre hatnak, egszen a fjl vgig. A teljes szintaktika az elfeldolgoz direktvkra a feltteles fordtstl eltekintve gy, hogy a mr megismert fogalmakat jra nem definiljuk, a kvetkez:
csoport: csoport-rsz csoport csoport-rsz csoport-rsz: elfeldolgoz-szimblumok jsor feltteles-fordts vezrl-sor elfeldolgoz-szimblumok: elfeldolgoz-szimblum elfeldolgoz-szimblumok elfeldolgoz-szimblum elfeldolgoz-szimblum: <fjlazonost> (csak #include direktvban) fjlazonost (csak #include direktvban) azonost (nincs kulcssz megklnbztets) konstans karakterlnc-konstans opertor elvlaszt-jel brmilyen nem fehr karakter, mely az elzek egyike sem vezrl-sor: #include elfeldolgoz-szimblumok jsor #define azonost <elfeldolgoz-szimblumok> jsor #define azonost(azonostlista) elfeldolgoz-szimblumok jsor #undef azonost jsor #line elfeldolgoz-szimblumok jsor #error <elfeldolgoz-szimblumok> jsor #pragma <elfeldolgoz-szimblumok> jsor # jsor jsor: soremels

Az elfeldolgoz-szimblum defincijban a fjlazonost krli <> a szintaktika rsze, s nem az elhagyhatsgot jelli, mint mskor. Ltszik, hogy a feldolgozsban brmely karakter, ami nem foglalt az elfeldolgoznak, szintn szintaktikai egysget kpez. 7.1 res (null) direktva A csak egy # karaktert tartalmaz sor. Ezt a direktvt elhagyja az elfeldolgoz, azaz hatsra nem trtnik semmi.

C programnyelv 7.2 #include direktva Az #include direktva lehetsges alakjait:


#include <fjlazonost> jsor #include fjlazonost jsor

121

mr a BEVEZETS S ALAPISMERETEK szakaszban rszleteztk. Tudjuk, hogy a vezrl-sort az elfeldolgoz a megadott fjl teljes tartalmval helyettesti. Magt a fjlt fjlazonost esetn elbb az aktulis knyvtrban (abban, ahonnt azt a fjlt tlttte, amelyikben ez az #include direktva volt), majd s <fjlazonost>s esetben a programfejleszt rendszerben elrt utakon keresi. A <> s az idzjelek kztt nincs makrhelyettests. Ha a fjlazonostban >, , , \, vagy /* karakterek vannak, az elfeldolgozs eredmnye meghatrozatlan. Ha macskakrms esetben a fjlazonost elrsi utat is tartalmaz, akkor a fjlt a preprocesszor csak abban a knyvtrban keresi, s sehol msutt. A direktva
#include elfeldolgoz-szimblum jsor

formjt elbb feldolgozza az elfeldolgoz, de a helyettestsnek itt is <>, vagy alakot kell eredmnyeznie, s a hats ennek megfelel. Pldul:
#define ENYIM \Cfajlok\Munka16\Pipo.h /* . . . */ #include ENYIM

Az #include direktvk egymsba gyazhatk, azaz a behozott fjl jabb #includeokat tartalmazhat, s azok megint jabbakat, s gy tovbb. Az egymsba gyazgatsokkal azonban vigyzni kell, mert egyes programfejleszt rendszerek ezek szintjt pldul 10ben korltozhatjk! 7.3 Egyszer #define makr A #define direktva makrt definil (makrdefinci). A makr szimblumhelyettest mechanizmus fggvnyszer formlis paramterlistval vagy anlkl. Elbb a paramter nlkli esettel foglalkozunk! Ilyenkor a direktva alakja:
#define azonost <elfeldolgoz-szimblumok> jsor

122

ELFELDOLGOZ DIREKTVK

Hatsra az elfeldolgoz a forrskdban ez utn kvetkez minden makrazonost elfordulst helyettest a lehet, hogy res elfeldolgozszimblumokkal. Ha res elfeldolgoz-szimblumokkal trtnik a helyettests, a makrazonost akkor is definilt, azaz #if defined vagy #ifdef direktvval "rkrdezhetnk" az azonostra, de a makrazonost minden forrsszvegbeli elfordulsnak eltvoltst jelenti tulajdonkpp. Nem trtnik meg a helyettests, ha a makrazonost karakter vagy karakterlnc konstansban, vagy megjegyzsben, vagy ms makrazonost rszeknt tallhat meg. Ezt a folyamatot makrkifejtsnek (expansion) nevezik. A elfeldolgoz-szimblumokat szoks makrtest nvvel is illetni. Pldul:
#define HI J napot! #define begin { #define end } #define then #define NIL #define EGY 1 int main(void) begin /* Helyettests {-re. */ int i=8, k=i+EGY;/* Csere k=i+1-re.*/ puts(HI); /* puts(J napot!); lesz belle */ puts(NIL); /* puts(); lesz a sorbl. */ puts(then); /* Nincs helyettests, mert a makrazonost karakterlnc konstansban van. */ if(++i<k) /* A sor eleji then semmire helyettestse, de a */ then puts(Ez a then-g!\n); /* msik marad.*/ else puts(Ez az else-g!\n); return 0; end /* Csere }-re. */

A makrkifejts utni makrazonostkat is helyettesti a preprocesszor, azaz a makrk is egymsba gyazhatk. A makrazonost jradefinilsa csak akkor nem hiba, ha az elfeldolgoz-szimblumok tkletesen, pozcihelyesen azonosak. Ettl eltr jradefinils csak a rvonatkoz #undef direktva utn lehetsges. A nyelv kulcsszavait is alkalmazhatjuk makrdefincikban, legfeljebb kiss rtelmetlennek tekinthet az eljrsunk:
#define LACI for #define PAPI while #define int long

, de a kvetkez fejezetben ismertetett, szabvnyos, elredefinilt makrk nem jelenhetnek meg sem #define, sem #undef direktvkban.

C programnyelv

123

A programfejleszt rendszer segtsgben clszer utna nzni, hogy mg milyen ms, vdett makrazonostk hasznlata tiltott! 7.4 Elredefinilt makrk Nhny makr elredefinilt az elfeldolgoz rendszerben, s kifejtetskkel specilis informci kpezhet. Ezek a makrk mind defined tpusak. Nem tehetk definilatlann, s nem definilhatk jra. A szabvnyos, elredefinilt makrk s jelentsk a kvetkez: __DATE__: HHH NN alak karakterlnc, s az aktulis forrsfjl elfeldolgozsa kezdetnek dtuma. A HHH hrombets hnapnv rvidts (Jan, Feb stb.). Az NN 1 s 31 kztti napszm, s gy tovbb. __FILE__: Karakterlncknt a feldolgozs alatt ll forrsfjl azonostjt tartalmazza. A makr vltozik #include s #line direktva hatsra, valamint ha a forrsfjl fordtsa befejezdik. __LINE__: Decimlis rtkknt a feldolgozs alatti forrsfjl aktulis sornak sorszma. A sorszmozs 1-tl indul. Mdosthatja pldul a #line direktva is. __STDC__: Definilt s 1, ha ANSI kompatibilis fordts van, msklnben definilatlan. __TIME__: OO:PP:MM alak karakterlnc, s a forrsfjl feldolgozsa megkezdsnek idejt tartalmazza. 7.5 #undef direktva
#undef azonost jsor

A direktva definilatlann teszi a makrazonostt, azaz a tovbbiakban nem vesz rszt a makrkifejtsben. Azt, hogy egy makrazonost definilt-e vagy sem a forrskdban, megtudhatjuk a
#ifdef azonost #ifndef azonost

direktvk segtsgvel, azaz a makrdefincinl ajnlhat a kvetkez stratgia:


#ifndef MAKROKA #define MAKROKA 162 #endif

Az ismeretlen makrazonostra kiadott #undef direktvt nem tekinti hibnak az elfeldolgoz.

124

ELFELDOLGOZ DIREKTVK

A definilatlann tett makrazonost ksbb jradefinilhat akr ms elfeldolgoz-szimblumokkal. A #define-nal definilt s kzben #undef-fel definilatlann nem tett makrazonost definilt marad a forrsfjl vgig. Pldul:
#define BLOKK_MERET 512 /* . . . */ puff = BLOKK_MERET*blkszam; /* Kifejts: 512*blkszam. */ /* . . . */ #undef BLOKK_MERET /* Innt a BLOKK_MERET ismeretlen makrazonost. */ /* . . . */ #define BLOKK_MERET 128 /* . . . */ puff = BLOKK_MERET*blkszam; /* Kifejts: 128*blkszam. */

7.6 Paramteres #define direktva A direktva alakja ilyenkor:


#define azonost(azonostlista) elfeldolgoz-szimblumok jsor

Az azonostlista egymstl vesszvel elvlasztott formlis paramterazonostkbl ll. A makrt hv aktulis paramterlistban ugyanannyi paramternek kell lennie, mint amennyi a formlis paramterlistban volt, mert klnben hibazenetet kapunk. Ugyan a makrra is a fggvnnyel kapcsolatos fogalmakat hasznljuk kpszersgk vgett, de ki kell hangslyozni, hogy a makr nem fggvny! A makrazonost s a paramterlistt nyit kerek zrjel kz semmilyen karakter sem rhat, hisz rgtn egyszer #define-n tenn a paramteres direktvt! Az elfeldolgoz elbb a makrazonostt helyettesti, s csak aztn a zrjelbe tett paramtereket:
Definci: Forrssor: Kifejtve: #define KOB(x) ((x)*(x)*(x)) n = KOB(y); n = ((y)*(y)*(y));

A ltszlag redundns zrjeleknek nagyon fontos szerepk van:


Definci: Forrssor: Kifejtve: #define KOB(x) (x*x*x) n = KOB(y + 1); n = (y + 1*y + 1*y + 1);

A kls zrjel pr a kifejezsekben val felhasznlhatsgot biztostja:


Definci: Forrssor: #define SUM(a,b) (a)+(b) n = 14.5 * SUM(x*y, z-8);

C programnyelv
Kifejtve: n = 14.5 * (x*y)+(z-8);

125

A zrjelbe, vagy aposztrfok, idzjelek kz tett vesszk nem minslnek a listban azonost elvlasztnak:
Definci: Forrssor: Kifejtve: Definci: Forrssor: Kifejtve: #define SUM(a,b) ((a)+(b)) return SUM(f(i,j), g(k,l)); return ((f(i,j))+(g(k,l))); #define HIBA(x,lanc) hibaki(Hiba: ,x,lanc) HIBA(2,ssn Enter-t, s Esc-t!); hibaki(Hiba: ,2,ssn Enter-t, s Esc-t!);

j azonost generlsi cllal a szimblum1##szimblum2 alakbl szimblum1szimblum2-t llt el az elfeldolgoz:


Definci: Forrssor: Kifejtve: #define VAR(i,j) (i##j) VAR(a,8) (a8)

A formlis paramter el rt # (gy nevezett karakterlncost opertor) az aktulis paramtert karakterlnc konstanss konvertlja:
Definci: Forrssor: Kifejtve: #define NYOM(jel) printf(#jel =%d\n, jel) int kilo = 100; NYOM(kilo); int kilo = 100; printf(kilo =%d\n, kilo);

Folytatssor most is sorvgi \ jellel kpezhet:


Definci: Forrssor: Kifejtve: #define FIGYU Ez igazbl egyetlen \ sornak minsl! puts(FIGYU); puts(Ez igazbl egyetlen sornak minsl!);

A makr nem fggvny, teht semmilyen ellenrzs sincs a paramterek adattpusra! Ha az aktulis paramter kifejezs, akkor kirtkelse tbbszr is megtrtnik:
int kob(int x){ return x*x*x;} #define KOB(x) ((x)*(x)*(x)) /* . . . */ int b = 0, a = 3; b = kob(a++); /* b == 27 s a == 4. */ a = 3; b = KOB(a++); /* Kifejtve: ((a++)*(a++)*(a++)), azaz b == 60 s a == 6. */

7.7 Karaktervizsgl fggvnyek (makrk) Megismerkedtnk az elz fejezetekben a makrk elnys, s persze htrnyos tulajdonsgaival. Mindezek dacra a makrk hasznlata a C ben elg szleskr.

126

ELFELDOLGOZ DIREKTVK

Nzzk csak meg a szabvnyos STDIO.H fejfjlt, s ltni fogjuk, hogy a szabvny bemenetet s kimenetet kezel getchar s putchar rutinok makrk. A szabvnyos STDIO.H fejfjlban deklarlt fggvnyek karakterosztlyozst vgeznek. A rutinok c paramtere int ugyan, de az rtknek unsigned char tpusban brzolhatnak, vagy EOFnak kell lennie. A visszatrsi rtkk ugyancsak int logikai jelleggel, azaz nem zrus (igaz), ha a feltett krdsre adott vlasz igen, ill. zrus (hamis), ha nem. A teljessg ignye nlkl felsorolunk nhny karakterosztlyoz fggvnyt! Fggvny islower(c) isalpha(c) isdigit(c) c kisbete? islower(c) | isupper(c) c decimlis szmjegye? isupper(c) c nagybete? Krds

isalnum(c) isalpha(c) | isdigit(c) isxdigit(c) c hexadecimlis szmjegye? isspace(c) isprint(c) c fehr karaktere? (szkz, soremels, lapemels, kocsi vissza, fggleges vagy vzszintes tabultor) c nyomtathat karaktere?

Meg kell mg emlteni kt konverzis rutin is: int tolower(c); int toupper(c); , melyek c rtkt kisbetv (tolower), ill. nagybetv (toupper) alaktva szolgltatjk, ha c bet karakter, de vltozatlanul adjk vissza, ha nem az. rjuk t PELDA17.Ct gy, hogy karaktervizsgl fggvnyeket hasznljon!
/* PELDA19.C: A bemenet karaktereinek leszmllsa kategrinknt az is fggvnyek segtsgvel. */ #include <stdio.h> #include <ctype.h> void main(void){ short k, num=0, feher=0, egyeb=0; printf("Bemeneti karakterek leszmllsa\n" "kategrinknt EOF-ig, vagy Ctrl+Z-ig.\n");

C programnyelv
while((k=getchar())!=EOF) if(isdigit(k)) ++num; else if (isspace(k)) ++feher; else ++egyeb; printf("Karakter szmok:\n----------------\n" "numerikus: %5hd\nfehr: %5hd\n" "egyb: %5hd\n----------------\n" "ssz: %10ld\n", num, feher, egyeb, (long)num+feher+egyeb); }

127

rjuk mg t ugyanebben a szellemben a PELDA18.C egesze fggvnyt is!


#include <ctype.h> #define HSZ sizeof(int)/sizeof(short)*5 int egesze(char s[]){ int i = 0, kezd; while(isspace(s[i])) ++i; if(s[i]=='+' || s[i]=='-') ++i; kezd=i; /* A szmjegyek itt kezddnek. */ while(isdigit(s[i]) && i-kezd<HSZ) ++i; if(kezd==i || !isspace(s[i]) && s[i]!=0) return 0; else return 1; }

A PELDA13.Cbeli strup rutin gy mdosulna:


#include <ctype.h> void strup(char s[]){ int i; for(i=0; s[i]; ++i) s[i]=toupper(s[i]); }

Milyen elnyei vannak a karakterosztlyoz fggvnyek hasznlatnak? A kd rvidebb, s ez ltal gyorsabb is. A program portbilis lesz, hisz fggetlenedik az ASCII (vagy ms) kdtbla sajtossgaitl. Az olvas utols logikus krdse mr csak az lehet, hogy mirt pont a makrk kztt trgyaljuk a karaktervizsgl rutinokat? Tbb C implementci makrknt valstja meg ezeket a fggvnyeket. Erre mutatunk itt be egy szintn nem teljes kr pldt azzal a felttellel, hogy a CHAR_BIT (bitek szma a char tpusban lsd LIMITS.Ht!) makr rtke 8.
/* Bitmaszk rtkek a lehetsges karaktertpusokra: */ #define _UPPER 0x1 /* Nagybet. */ #define _LOWER 0x2 /* Kisbet. */ #define _DIGIT 0x4 /* Decimlis szmjegy. */ #define _SPACE 0x8 /* \t,\r,\n,\v,\f */ #define _PUNCT 0x10 /* Elvlaszt-jel. */

128

ELFELDOLGOZ DIREKTVK

#define _CONTROL 0x20 /* Vezrl karakter. */ #define _BLANK 0x40 /* Szkz. */ #define _HEX 0x80 /* Hexadecimlis szmjegy. */ /* Globlis tmb, melyben a rendszer mindenegyes kdtbla pozcira belltotta ezeket a biteket: */ extern unsigned char _ctype[]; /* Nhny makr: */ #define islower(_c) (_ctype[_c]&_LOWER) #define isupper(_c) (_ctype[_c]&_UPPER) #define isalpha(_c) (_ctype[_c]&(_UPPER|_LOWER)) #define isdigit(_c) (_ctype[_c]&_DIGIT) #define isalnum(_c) (_ctype[_c]&(_UPPER|_LOWER|_DIGIT)) #define isxdigit(_c) (_ctype[_c]&_HEX) #define isspace(_c) (_ctype[_c]&(_SPACE|_BLANK)) #define isprint(_c) (_ctype[_c]&(_BLANK|_PUNCT|_UPPER|\ _LOWER|_DIGIT))

Megoldand feladatok: Ksztse el a kvetkez fggvnyek makr vltozatt! A fejezetben emltett tolowert s touppert. A TPUSOK S KONSTANSOK szakaszban megrt strcopyt, s ms karakterlnc kezelkt. Ha az olvasban felmerlt volna az a gondolat, hogy mi van akkor, ha ugyanolyan nev makr s fggvny is ltezik, akkor arra szeretnnk emlkeztetni, hogy: Az elfeldolgozs mindig a fordts eltt trtnik meg, s gy mindenbl makr lesz. Ha #undef direktvval definilatlann tesszk a makrt, akkor attl kezdve csak fggvny lesz a forrsszvegben. Ha a hvsban redundns zrjelbe zrjuk a makr vagy a fggvny nevt, akkor az elfeldolgoz ezt nem fejti ki, teht bizonyosan fggvnyhvs lesz belle.
...(makrnv)(paramterek)...

7.8

Feltteles fordts

feltteles-fordts: if-csoport <elif-csoportok> <else-csoport> endif-sor if-csoport: #if konstans-kifejezs jsor <csoport> #ifdef azonost jsor <csoport> #ifndef azonost jsor <csoport>

C programnyelv
elif-csoportok: elif-csoport elif-csoportok elif-csoport elif-csoport: #elif konstans-kifejezs jsor <csoport> else-csoport: #else jsor <csoport> endif-sor: #endif jsor jsor: soremels

129

A feltteles direktvk szerint kihagyand forrssorokat az elfeldolgoz trli a forrsszvegbl, s a feltteles direktvk sorai maguk pedig kimaradnak az eredmny fordtsi egysgbl. A feltteles direktvk ltal kpzett konstrukcit - melyet rgtn bemutatunk egy ltalnos pldn mindenkppen be kell fejezni abban a forrsfjlban, amelyben elkezdtk.
#if konstans-kifejezs1 <szekci1> <#elif konstans-kifejezs2 <szekci2>> /* . . . */ <#elif konstans-kifejezsN <szekciN>> <#else <vgs-szekci>> #endif

Lssuk a kirtkelst! 1, Ha a konstans-kifejezs1 rtke nem zrus (igaz), akkor a preprocesszor a szekci1 sorait feldolgozza, s az eredmnyt tadja a fordtnak. A szekci1 termszetesen res is lehet. Ezutn az ezen #if-hez tartoz sszes tbbi sort a vonatkoz #endif-fel bezrlag kihagyja, s az #endif-t kvet sorral folytatja a munkt az elfeldolgoz. 2, Ha a konstans-kifejezs1 rtke zrus (hamis), akkor a preprocesszor a szekci1-t teljes egszben elhagyja. Teht nincs makrkifejts, s nem adja t a feldolgozott darabot a fordtnak! Ezutn viszont a kvetkez #elif konstans-kifejezse kirtkelsbe fog, s gy tovbb. 3, sszestve az #if-en s az #elif-eken lefel haladva az a szekci kerl elfeldolgozsra, s ennek eredmnye fordtsra, melynek konstanskifejezse igaznak bizonyul. Ha egyik ilyen konstans-kifejezs sem igaz, akkor az #else vgs-szekcijra vonatkoznak az elbbiekben mondottak.

130

ELFELDOLGOZ DIREKTVK

Az #if . . . #endif konstrukcik tetszleges mlysgben egymsba gyazhatk. Az #if . . . #endif szerkezetbeli konstans-kifejezseknek korltozott, egsz tpusaknak kell lennik! Konkrtabban egsz konstanst, karakter llandt tartalmazhat a kifejezs, s benne lehet a defined opertor is. Tilos hasznlni viszont benne explicit tpuskonverzit, sizeof kifejezst, enumertort s lebegpontos konstanst, mint norml egsz tpus konstans kifejezsekben! Az elfeldolgoz kznsges makrhelyettestsi menettel dolgozza fel a konstans-kifejezseket. 7.8.1 A defined opertor Makrazonostk definiltsgnak ellenrzsre val, s csak #if s #elif konstans-kifejezseiben szerepelhet. A
defined(azonost)

vagy a
defined azonost

alak a makrazonost definiltsgra krdez r. Miutn a vlasz logikai rtk a defined szerkezetek logikai mveletekkel is kombinlhatk a konstans-kifejezsekben. Pldul:
#if defined(makro1) && !defined(makro2)

Ha biztostani szeretnnk azt, hogy a fordtsi egysgbe egy bizonyos fejfjl (legyen HEADER.H) csak egyszer pljn be, akkor a fejfjl szvegt kvetkezkpp kell direktvkba foglalni:
#if !defined(_HEADERH) #define _HEADERH /* Itt van a fejfjl szvege. */ #endif

Ilyenkor akrhny #include is jn a forrsfjlban a HEADER.H fejfjlra, a behozatala csak elszr trtnik meg, mert a tovbbi bekapcsolsokat az _HEADERH makr definiltsga megakadlyozza. Nzznk csak bele nhny szabvnyos fejfjlba, ott is alkalmazzk ezt a konstrukcit! 7.8.2 Az #ifdef s az #ifndef direktvk Az #ifdef direktva egy makrazonost definiltsgra, s az #ifndef viszont a definilatlansgra krdez r, azaz:
#ifdef azonost

#if defined(azonost)

C programnyelv
#ifndef azonost

131

#if !defined(azonost)

7.9

#line sorvezrl direktva

#line egsz-konstans <fjlazonost> jsor

Jelzi az elfeldolgoznak, hogy a kvetkez forrssor egsz-konstans sorszm, s a fjlazonost nev fjlbl szrmazik. Miutn az aktulisan feldolgozs alatt ll forrsfjlnak is van azonostja a fjlazonost paramter elhagysakor a #line az aktulis fjlra vonatkozik. A makrkifejts a #line paramtereiben is megtrtnik. Vegynk egy pldt!
/* PELDA.C: a #line direktvra: */ #include <stdio.h> #line 4 PIPI.C void main(void) { printf(\nA(z) %s fjl %d sorban vagyunk!, __FILE__, __LINE__); #line 12 PELDA.C printf(\n); printf(A(z) %s fjl %d sorban vagyunk!, __FILE__, __LINE__); #line 8 printf(\n); printf(A(z) %s fjl %d sorban vagyunk!\n, __FILE__, __LINE__); }

Az ellltott standard kimenet a kvetkez lehet:


#line 1 "pelda.c" #line 1 "c:\\msdev\\include\\stdio.h" . . . #line 524 "c:\\msdev\\include\\stdio.h" #line 3 "pelda.c" #line 4 "PIPI.C" void main(void) { printf(\nA(z) %s fjl %d sorban vagyunk!, PIPI.C, 6); #line 12 "PELDA.C printf(\n); printf(A(z) %s fjl %d sorban vagyunk!, PELDA.C, 13); #line 8 "PELDA.C" printf(\n); printf(A(z) %s fjl %d sorban vagyunk!\n,PELDA.C, 9); }

132

ELFELDOLGOZ DIREKTVK

A #line direktva tulajdonkppen a __FILE__ s a __LINE__ elredefinilt makrk rtkt lltja. Ezek a makrrtkek a fordt hibazeneteiben jelennek meg. Szval a direktva diagnosztikai clokat szolgl. 7.10 #error direktva
#error <hibazenet> jsor

direktva zenetet generl, s befejezdik a fordts. Az zenet alakja lehet a kvetkez:


Error: fjlazonost sorszm: Error directive: hibazenet

Rendszerint #if direktvban hasznlatos. Pldul:


#if (SAJAT!=0 && SAJAT!=1) #error A SAJAT 0-nak vagy 1-nek definiland! #endif

7.11 #pragma direktvk


#pragma <elfeldolgoz-szimblumok> jsor

A direktvk gp s opercis rendszerfggk. Bennk a #pragma kulcsszt kvet szimblumok mindig objektumai a makrkifejtsnek, s tulajdonkppen specilis fordti utastsok, s ezek paramterei. Az elfeldolgoz a fel nem ismert #pragma direktvt figyelmen kvl hagyja.

C programnyelv

133

8 OBJEKTUMOK S FGGVNYEK
Az azonostk rtelmt a deklarcik rgztik. Tudjuk, hogy a deklarci nem jelent szksgkppen memriafoglalst. Csak a defincis deklarci ilyen.
deklarci: deklarci-specifiktorok<init-deklartorlista> init-deklartorlista: init-deklartor init-deklartorlista, init-deklartor init-deklartor: deklartor deklartor=inicializtor

Az inicializtorokkal s inicializtorlistkkal a BEVEZETS S ALAPISMERETEK szakasz Inicializls fejezetben foglalkoztunk. A deklartorok a deklarland neveket tartalmazzk. A deklarci-specifiktorok tpus s trolsi osztly specifiktorokbl llnak:
deklarci-specifiktorok: trolsi-osztly-specifiktor<deklarci-specifiktorok> tpusspecifiktor<deklarci-specifiktorok> tpusmdost<deklarci-specifiktorok>

A tpusspecifiktorokat a TPUSOK S KONSTANSOK szakaszban trgyaltuk, s ezek kzl kivettk a const s a volatile tpusmdostkat. 8.1 Objektumok attribtumai Az objektum egy azonosthat memria terletet, mely konstans vagy vltoz rtk(ek)et tartalmaz. Az objektum egyik attribtuma (tulajdonsga) az adattpusa. Tudjuk, hogy van ezen kvl azonostja (neve) is. Az objektum adattpus attribtuma rgzti az objektumnak allokland (lefoglaland) memria mennyisgt s a benne trolt informci belsbrzolsi formjt. Az objektum neve nem attribtum, hisz klnfle hatskrben tbb klnbz objektumnak is lehet ugyanaz az azonostja. Az objektum tovbbi attribtumait (trolsi osztly, hatskr, lthatsg, lettartam, stb.) a deklarcija s annak a forrskdban elfoglalt helye hatrozza meg. Emlkeztetl: a loklis, globlis s a bels, kls vltozkat taglaltuk mr a BEVEZETS S ALAPISMERETEK szakaszban!

134

OBJEKTUMOK S FGGVNYEK

8.1.1 Trolsi osztlyok Az objektumokhoz rendelt azonostknak van legalbb trolsi osztly s adattpus attribtuma. Szoks e kettt egytt is adattpusnak nevezni. A trolsi osztly specifiktor defincija:
trolsi-osztly-specifiktor: auto register extern static typedef

A trolsi osztly meghatrozza az objektum lettartamt, hatskrt s kapcsoldst. Egy adott objektumnak csak egy trolsi osztly specifiktora lehet egy deklarciban. A trolsi osztlyt a deklarci forrskdbeli elhelyezse implicit mdon rgzti, de a megfelel trolsi osztly kulcssz expliciten is belerhat a deklarciba. A kapcsoldssal kln fejezetben foglalkozunk. Trolsi osztly kulcssz nlkli deklarcik esetben a blokkon bell deklarlt objektum mindig auto definci, s a fggvny pedig extern deklarci. A fggvnydefincik s az ezeken kvli objektum s fggvnydeklarcik mind extern, statikus trolsi osztlyak. Ktfle trolsi osztly van. 8.1.1.1 Automatikus (auto, register) trolsi osztly Az ilyen objektumok loklis lettartamak, s loklisak a blokk egy adott pldnyra. Az ilyen deklarcik defincik is egyben, azaz megtrtnik a memriafoglals is. Ismeretes, hogy a fggvnyparamterek is automatikus trolsi osztlyaknak minslnek. Rekurzv kd esetn az automatikus objektumok garantltan klnbz memria terleten helyezkednek el mindenegyes blokkpldnyra. A C az automatikus objektumokat a program vermben trolja, s gy alaprtelmezett kezdrtkk "szemt". Expliciten inicializlt, loklis automatikus objektum esetben a kezdrtk ads viszont mindannyiszor

C programnyelv

135

megtrtnik, valahnyszor bekerl a vezrls a blokkba. A blokkon bell definilt objektumok auto trolsi osztlyak, hacsak ki nem rtk expliciten az extern vagy a static kulcsszt a deklarcijukban. Csak loklis hatskr objektumok deklarcijban hasznlhat az auto trolsi osztly specifiktor. Az auto kulcsszt tilos kls deklarciban vagy definciban alkalmazni! Az automatikus trolsi osztly objektumok loklis lettartamak s nincs kapcsoldsuk. Miutn ez az alaprtelmezs az sszes loklis hatskr objektum deklarcijra, nem szoks s szksgtelen expliciten kirni. Az auto trolsi osztly specifiktor fggvnyre nem alkalmazhat! Az automatikus trolsi osztly specilis vlfaja a regiszteres. A register kulcssz a deklarciban azt jelzi a fordtnak, hogy a vltozt nagyon gyakran fogjuk hasznlni, s krjk, hogy az illet objektumot regiszterben helyezze el, ha lehetsges. A regiszteres trols rvidebb gpi kd programot eredmnyez, hisz elmarad a memribl regiszterbe (s vissza) tltgets. Emiatt, s mert a regiszter a memrinl jval kisebb elrsi idej, a szoftver futsa is gyorsul. A hardvertl fgg ugyan, de valjban csak kevs objektum helyezkedhet el regiszterben, s csak meghatrozott tpus vltozk kerlhetnek oda. A fordt elhagyja a register kulcsszt a felesleges s a nem megfelel tpus deklarcikbl, azaz az ilyen vltozk csak norml, automatikus trolsi osztlyak lesznek. A regiszteres objektumok loklis lettartamak, s ekvivalensek az automatikus vltozkkal. Csak loklis vltozk s fggvnyparamterek deklarcijban alkalmazhat a register kulcssz. Kls deklarciban vagy definciban a register kulcsszt tilos alkalmazni! Fggetlenl attl, hogy a register vltoz igazn regiszterben helyezkedik el, vagy sem, tilos a cmre hivatkozni! A globlis optimalizlst bekapcsolva a fordt figyelmen kvl hagyja a programoz register ignyeit, s sajt maga vlaszt regiszter ki-

136

OBJEKTUMOK S FGGVNYEK

osztst, de minden ms a register kulcsszhoz kapcsold szemantikt tekintetbe vesz. rjunk int prime(int x) fggvny, mely eldnti pozitv egsz paramterrl, hogy prmszme! A prmszm csak 1gyel s nmagval oszthat maradk nlkl. Prbljuk meg teht egsz szmok szorzataknt ellltani. Ha sikerl, akkor nem trzsszmrl van sz. A szm prm viszont, ha ez nem megy. Indtunk teht 2rl mindig nvelgetve egy osz vltozt, s megprbljuk, hogy oszthate vele maradk nlkl az x. Meddig nvekedhet az osz? x ngyzetgykig, mert a kt szorztnyezre bontsnl fordtott arnyossg van a kt tnyez kztt.
int prime(register x){ register osz = 2; if(x < 4) return 1; while(osz*osz <= x){ if(!(x%osz)) return 0; ++osz; if(!(osz&1)) ++osz; } return 1; }

A prime paramtere s az osz loklis vltoz register int tpus programgyorstsi cllal. A fggvny utols eltti sorbl ltszik, hogy legalbb a pros szmokat nem prbljuk ki osztknt, miutn 2vel nem volt maradk nlkl oszthat az x. Az olvasra bzzuk, hogy ksrje meg mg gyorstani az algoritmust! Ksztsnk programot, mely megllaptja egy vals szmsorozat tlagt s azt, hogy hny tlagnl kisebb s nagyobb eleme van a sorozatnak! A vals szmokat a szabvny bemenetrl kell beolvasni! Egy sorban egyet! A sorozat megadsnak vgt jelentse res sor rkezse a bemenetrl! Kezdjk a kdolst az int lebege(char s[]) fggvnnyel, mely megllaptja karakterlnc paramterrl, hogy formlisan helyes lebegpontos szme! A karaktertmb elejn lev fehr karaktereket t kell lpni, s a numerikus rsz ugyancsak fehr, vagy lnczr zrus karakterrel zrul. A lebegpontos szmnak ki kell egybknt elgtenie a lebegpontos konstans rsszablyt!
#include <ctype.h> int lebege(char s[]){ int i=0, kezd; /* Fehr karakterek tlpse a lnc elejn: */ while(isspace(s[i])) ++i; /* A mantissza eljele: */

C programnyelv

137

if(s[i]=='+'||s[i]=='-') ++i; kezd=i; /* A szjegyek itt kezddnek. */ /* A mantissza egsz rsze: */ while(isdigit(s[i])) ++i; /* A mantissza trt rsze: */ if(s[i]=='.') ++i; while(isdigit(s[i])) ++i; /* Nincs szmjegy, vagy csak egy . van: */ if(i==kezd||kezd+1==i&&s[kezd]=='.') return 0; /* Kitev rsz: */ if(toupper(s[i])=='E'){ ++i; if(s[i]=='+'||s[i]=='-')++i; /* Egy szmjegynek lennie kell a kitevben! */ if(!isdigit(s[i])) return 0; while(isdigit(s[i])) ++i;} /* Vge: */ if(isspace(s[i])||!s[i]) return 1; else return 0; }

8.1.1.2 Statikus (static, extern) trolsi osztly A statikus trolsi osztly objektumok ktflk: blokkra loklisak, vagy blokkokon t klsk. Az ilyen trolsi osztly objektumok statikus lettartamak. Brhogyan is: megrzik rtkket az egsz program vgrehajtsa sorn, akrhnyszor is hagyja el a vezrls az ket tartalmaz blokkot, s tr oda vissza. Fggvnyen, blokkon bell gy definilhat statikus trolsi osztly vltoz, hogy a deklarcijba ki kell expliciten rni a static kulcsszt. A static kulcsszavas deklarci definci. Ksztsnk double gyujto(double a) fggvnyt, mely gyjti aktulis paramterei rtkt! Mindig az eddig megllaptott sszeget adja vissza. Ne legyen bamba, azaz ne lehessen vele ttrni az brzolsi hatrokat! A lebegpontos tl, vagy alulcsorduls futsidej hiba!
#include <float.h> double gyujto(double a){ static double ossz=0.0; if(a<0.0&&-(DBL_MAX+a)<ossz || DBL_MAX-a>ossz) ossz+=a; return ossz; }

138

OBJEKTUMOK S FGGVNYEK

Ltszik, hogy nincs sszegzs, ha az ossz DBL_MAXhoz, vagy +DBL_MAXhoz a tvolsgon bellre kerl. Rekurzv kdban a statikus objektum llapota garantltan ugyanaz minden fggvnypldnyra. Explicit inicializtorok nlkl a statikus vltozk minden bitje zrus kezdrtket kap. Az implicit s az explicit inicializls mg loklis statikus objektum esetn is csak egyszer trtnik meg, a program indulsakor. A gyujtoben teljesen felesleges zrussal inicializlni a statikus ossz vltozt, hisz implicit mdon is ez lenne a kezdrtke. A fggvnydefincikon kvl elhelyezett, trolsi osztly kulcssz nlkli deklarcik kls, statikus trolsi osztly objektumokat definilnak, melyek globlisak az egsz programra nzve. A kls objektumok statikus lettartamak. Az explicit mdon extern trolsi osztlynak deklarltak olyan objektumokat deklarlnak, melyek defincija nem ebben a fordtsi egysgben van, vagy befoglal hatskrben tallhat.
extern int MasholDefinialt; /* Ms ford. egysgben */ void main(){ int IttDefinilt; { extern int IttDefinilt; /* A befoglal hatskrbeli IttDefinilt-ra val hivatkozs. */ } }

A kls kapcsoldst jellend az extern fggvny s objektum fjl s loklis hatskr deklarciiban hasznlhat. Fjl hatskr vltozk s fggvnyek esetben ez az alaprtelmezs, teht expliciten nem szoks kirni. Az extern kulcssz explicit kirsa tilos a vltoz definil deklarcijban! A kls objektumok s a fggvnyek is deklarlhatk staticnek, amikor is lokliss vlnak az ket tartalmaz fordtsi egysgre, s minden ilyen deklarci definci is egyben. Folytatva a pldnkat: a szabvny bemenetrl rkez, vals szmokat valahol trolni kne, mert az tlag csak az sszes elem beolvassa utn llapthat meg. Ez utn jra vgig kell jrni a szmokat, hogy kiderthessk, hny tlag alatti s feletti van kztk. A vltozatossg kedvrt, s mert a clnak tkletesen megfelel, hasznljunk vermet a letrolshoz, melyet s kezel fggvnyeinek definciit

C programnyelv

139

helyezzk el a DVEREM.C forrsfjlban, s a ms fordtsi egysgbl is hvhat fggvnyek prototpusait tegyk be a DVEREM.H fejfjlba! Egy tmakr adatait s kezel fggvnyeit egybknt is szoks a C ben kln forrsfjlban (gy nevezett implementcis fjlban) elhelyezni, vagy a lefordtott vltozatot kln knyvtrfjlba tenni. A dologhoz mindig tartozik egy fejfjl is, mely tartalmazza legalbb a tmakr ms forrsmodulbl is elrhet adatainak deklarciit, s kezel fggvnyeinek prototpusait. Implementcis fjl esetn a fejfjlban mg tpusdefincik, szimbolikus llandk, makrk, stb. szoktak lenni.
/* DVEREM.H: double verem push, pop s clear fggvnyenek prototpusai. */ int clear(void); double push(double x); double pop(void); /* DVEREM.C: double verem push, pop s clear fggvnyekkel. */ #define MERET 128 /* A verem mrete. */ static int vmut; /* A veremmutat. */ static double v[MERET]; /* A verem. */ int clear(void){ vmut=0; return MERET; } double push(double x){ if(vmut<MERET) return v[vmut++]=x; else return x+1.; } double pop(void){ if(vmut>0) return v[--vmut]; else return 0.; }

A vmut veremmutat, s a v verem statikus ugyan, de loklis a DVEREM.C fordtsi egysgre. Ltszik, hogy a push x paramtervel tlti a vermet, s sikeres esetben ezt is adja vissza. Ha a verem betelt, ms rtk jn vissza tle. A pop visszaszolgltatja a legutbb betett rtket, ill. az res verembl mindig zrussal tr vissza. A clear trli a veremmutatt, s ez ltal a vermet, s visszaadja a verem maximlis mrett. Kdoljuk le vgre az eredetileg kitztt feladatot!
/* PELDA20.C: Vals szmok tlaga, s az ez alatti, ill. feletti elemek szma. */ #include <stdio.h> #include <stdlib.h> /* Az atof miatt! */ #define INP 60 /* Az input puffer mrete. */ int getline(char s[],int lim); #include <ctype.h> int lebege(char s[]);

140

OBJEKTUMOK S FGGVNYEK

#include <float.h> double gyujto(double a); #include "DVEREM.H" void main(void){ int max=clear(), /* A verem max. mrete. */ i=0, alatt, felett; char s[INP+1]; /* Az input puffer. */ double a; printf( "Szmsorozat tlaga alatti s feletti " "elemeinek szma.\nA megadst res " "sorral kell befejezni!\n\n"); while( printf("%4d. elem: ", i+1), i<max && getline(s,INP)>0) if(lebege(s)){ push(a=atof(s)); printf("Az sszeg:%30.6f\n\n", a=gyujto(a)); ++i;} printf( "\nAz tlag: %30.6f\n", a/=i); for(max=alatt=felett=0; max<i; ++max){ double b=pop(); if(b<a) ++alatt; else if(b>a) ++felett; } printf("Az tlag alattiak szma: %8d.\n" "Az tlag felettiek szma:%8d.\n", alatt, felett); }

Vigyzat: a plda a PELDA20.Cbl s a DVEREM.Cbl kpzett prodzsekt segtsgvel futtathat csak! 8.1.2 lettartam (lifetime, duration) Az lettartam attribtum szorosan ktdik a trolsi osztlyhoz, s az a peridus a program vgrehajtsa kzben, mg a deklarlt azonosthoz objektumot allokl a fordt a memriban, azaz amg a vltoz vagy a fggvny ltezik. Megklnbztethetnk fordtsi idej s futsidej objektumokat. A vltozk pldul a tpusoktl s a tpusdefinciktl eltren futs idben vals, alloklt memrival rendelkeznek. Hrom fajta lettartam van. 8.1.2.1 Statikus (static vagy extern) lettartam Az ilyen objektumokhoz a memria hozzrendels a program futsnak megkezddsekor trtnik meg, s az allokci marad is a program befejezdsig. Minden fggvny statikus lettartam objektum brhol is defi-

C programnyelv

141

niljk ket. Az sszes fjl hatskr vltoz is ilyen lettartam. Ms vltozk a static vagy az extern trolsi osztly specifiktorok explicit megadsval tehetk ilyenn. A statikus lettartam objektumok minden memria bitje (a fggvnyektl eltekintve) zrus kezdrtket kap explicit inicializls hinyban. Ne keverjk ssze a statikus lettartamot a fjl (globlis) hatskrrel, ui. egy objektum loklis hatskrrel is lehet statikus lettartam, csak deklarcijban meg kell adni expliciten a static trolsi osztly kulcsszt. 8.1.2.2 Loklis (auto vagy register) lettartam Ezek az objektumok akkor jnnek ltre (allokci) a veremben vagy regiszterben, amikor a vezrls belp az ket magba foglal blokkba vagy fggvnybe, s meg is semmislnek (deallokci), mihelyt kikerl a vezrls innt. A loklis lettartam objektumok loklis hatskrek, s mindig explicit inicializlsra szorulnak, hisz ltrejvetelk helyn szemt van. Ne feledjk, hogy a fggvnyparamterek is loklis lettartamak! Az auto trolsi osztly specifiktor deklarciban val kirsval expliciten loklis lettartamv tehetnk egy vltozt, de erre tbbnyire semmi szksg sincs, mert blokkon vagy fggvnyen bell deklarlt vltozk esetben az alaprtelmezett trolsi osztly amgy is az auto. A loklis lettartam objektum egyben loklis hatskr is, hisz az t magba foglal blokkon kvl nem ltezik. A dolog megfordtsa nem igaz, mert loklis hatskr objektum is lehet statikus lettartam. Ha egy regiszterben is elfr vltozt (pldul char, short, stb. tpust) expliciten register trolsi osztlynak deklarlunk, akkor a fordt ehhez hozzrti automatikusan az auto kulcsszt is, hisz a vltozkat csak addig tudja regiszterben elhelyezni, mg azok el nem fogynak, s ezutn a veremben allokl nekik memrit. 8.1.2.3 Dinamikus lettartam Az ilyen objektumokhoz a Cben pldul a malloc fggvnnyel rendelhetnk memrit a heapen, amit aztn a freevel felszabadthatunk. Miutn a memriaalloklshoz knyvtri fggvnyeket hasznlunk, s ezek nem rszei a nyelvnek, gy a Cben nincs is dinamikus lettartam igazbl. A C++ban ugyanezen funkcikra megalkottk a new s a delete opertorokat, melyek rszei a nyelvnek.

142

OBJEKTUMOK S FGGVNYEK

8.1.3 Hatskr (scope) s lthatsg (visibility) A hatskr rvnyessgi tartomnynak is nevezik az azonost azon tulajdonsga, hogy vele az objektumot a program mely rszbl rhetjk el. Ez is a deklarci helytl s magtl a deklarcitl fgg attribtum. Felsoroljuk ket! 8.1.3.1 Blokk (loklis, bels) hatskr A deklarcis ponttl indul s a deklarcit magba foglal blokk vgig tart. Az ilyen hatskr vltozkat szoks bels vltozknak is nevezni. Loklis hatskrek a fggvnyek formlis paramterei is, s hatskrk a fggvnydefinci teljes blokkja. A blokk hatskr azonost hatskre minden a krdses blokkba begyazott blokkra is kiterjed. Pldul:
int fv(float lo){ double szamar; /* A loklis hatskr itt indul. */ /* . . . */ long double oszver; /* Az oszver hatskre innt (deklarcis pont) indul s a fggvnydefinci vgig tart. Szval nem lehetne a szamar vltozt az oszver-rel inicializlni. */ if (/* felttel */){ char lodarazs = l; /* A lodarazs hatskre ez a bels blokk. */ /* . . . */ } /* A lodarazs hatskrnek vge. */ /* . . . */ } /* A lo, szamar s oszver hatskrnek vge. */

8.1.3.2 Fggvny hatskr Ilyen hatskre csak az utasts cmknek van. Az utasts cmke ezen az alapon: fggvnyen belli egyedi azonost, melyet egy olyan vgrehajthat utasts el kell rni kettspontot kzbeszrva, melyre el kvnunk gazni. Pldul:
int fv(float k){ int i, j; /* . . . */ cimke: utasts; /* . . . */ if (/* felttel */) goto cimke; /* . . . */ }

8.1.3.3 Fggvny prototpus hatskr Ilyen hatskre a prototpusban deklarlt paramterlista azonostinak van, melyek teht a fggvny prototpussal be is fejezdnek. Pldul a

C programnyelv

143

kvetkez fggvnydefinciban az i, j s k azonostknak van fggvny prototpus hatskre:


void fv(int i, char j, float k);

Az i, j s k ilyen megadsnak semmi rtelme sincs. Az azonostk teljesen feleslegesek. A


void fv(int, char, float);

ugyanennyit mondott volna. Fggvny prototpusban neveket akkor clszer hasznlni, ha azok lernak valamit. Pldul a
double KamatOsszeg(double osszeg, double kamat, int evek);

az osszeg kamatos kamatt kzli evek vre. 8.1.3.4 Fjl (globlis, kls) hatskr A minden fggvny testn kvl deklarlt azonostk rendelkeznek ilyen hatskrrel, mely a deklarcis pontban indul s a forrsfjl vgig tart. Ez persze azt is jelenti, hogy a fjl hatskr objektumok a deklarcis pontjuktl kezdve minden fggvnybl s blokkbl elrhetk. A globlis vltozkat szoks kls vltozknak is nevezni. Pldul a g1, g2 s g3 vltozk ilyenek:
int g1 = 7; /* g1 fjl hatskre innt indul. */ void main(void) { /* ... */ } float g2; /* g2 fjl hatskre itt startol. */ void fv1(void) { /* ... */ } double g3 = .52E-40; /* Itt kezddik g3 hatskre */ void fv2(void) { /* ... */ } /* Itt van vge a forrsfjlnak s a g1, g2 s g3 kls vltozk hatskrnek. */

8.1.3.5 Lthatsg A forrskd azon rgija egy azonostra vonatkozan, melyben leglis mdon elrhet az azonosthoz kapcsolt objektum. A hatskr s a lthatsg tbbnyire fedik egymst, de bizonyos krlmnyek kztt egy objektum ideiglenesen rejtett vlhat egy msik ugyanilyen nev azonost feltnse miatt. A rejtett objektum tovbbra is ltezik, de egyszeren az azonostjval hivatkozva nem rhet el, mg a msik ugyanilyen nev azonost hatskre le nem jr. Pldul:
{ int i; char c = z; /* Az i s c hatskre indul. */ i = 3; /* int i-t rtk el. */ /* . . . */ { double i = 3.5e3; /* double i hatskre itt kezddik, s elrejti int i-t, br */

144
/* . . . */ c = A; } } ++i;

OBJEKTUMOK S FGGVNYEK
/* hatskre nem sznik meg. */ /* char c lthat s hatskre itt is tart. */ /* A double i hatskrnek vge */ /* int i s c hatskrben s lthatk. */ /* int i s char c hatskrnek vge. */

8.1.3.6 Nvterlet (name space) Az a hatskr, melyen bell az azonostnak egyedinek kell lennie. Ms nvterleten konfliktus nlkl ltezhet ugyanilyen azonost, a fordt kpes megklnbztetni ket. A nvterlet fajtkat a STRUKTRK S UNIK trgyalsa utn tisztzzuk majd! 8.1.4 Kapcsolds (linkage) A kapcsoldst csatolsnak is nevezik. A vgrehajthat program gy jn ltre, hogy tbb, klnll fordtsi egysget fordtunk, aztn a kapcsol-szerkesztvel (linker) sszekapcsoltatjuk az eredmny .OBJ fjlokat, ms meglv trgymodulokat s a knyvtrakbl szrmaz trgykdokat. Problma akkor van, ha ugyanaz az azonost klnbz hatskrkkel deklarlt - pldul ms-ms forrsfjlban - vagy ugyanolyan hatskrrel egynl tbbszr is deklarlt. A kapcsol-szerkeszts az a folyamat, mely az azonost minden elfordulst korrekt mdon egy bizonyos objektumhoz vagy fggvnyhez rendeli. E folyamat sorn minden azonost kap egy kapcsoldsi attribtumot a kvetkez lehetsgesek kzl: kls (external) kapcsolds, bels (internal) kapcsolds vagy nincs (no) kapcsolds. Ezt az attribtumot a deklarcik elhelyezsvel s formjval, ill. a trolsi osztly (static vagy extern) explicit vagy implicit megadsval hatrozzuk meg. Lssuk a klnfle kapcsoldsok rszleteit!

C programnyelv

145

A kls kapcsolds azonost minden pldnya ugyanazt az objektumot vagy fggvnyt reprezentlja a programot alkot minden forrsfjlban s knyvtrban. A bels kapcsolds azonost ugyanazt az objektumot vagy fggvnyt jelenti egy s csak egy fordtsi egysgben (forrsfjlban). A bels kapcsolds azonostk a fordtsi egysgre, a kls kapcsoldsak viszont az egsz programra egyediek. A kls s bels kapcsoldsi szablyok a kvetkezk: Brmely objektum vagy fggvnyazonost fjl hatskrrel bels kapcsolds, ha deklarcijban expliciten elrtk a static trolsi osztlyt. Az explicit mdon extern trolsi osztly objektum vagy fggvnyazonostnak ugyanaz a kapcsoldsa, mint brmely lthat fjl hatskr deklarcijnak. Ha nincs ilyen lthat fjl hatskr deklarci, akkor az azonost kls kapcsolds lesz. Ha fggvnyt explicit trolsi osztly specifiktor nlkl deklarlnak, akkor kapcsoldsa olyan lesz, mintha kirtk volna az extern kulcsszt. Ha fjl hatskr objektumazonostt deklarlnak trolsi osztly specifiktor nlkl, akkor az azonost kls kapcsolds lesz. A fordtsi egysg bels kapcsoldsnak deklarlt azonostjhoz egy s csak egy kls definci adhat meg. A kls definci olyan kls deklarci, mely az objektumhoz vagy fggvnyhez memrit is rendel. Ha kls kapcsolds azonostt hasznlunk kifejezsben (a sizeof operandustl eltekintve), akkor az azonostnak csak egyetlen kls defincija ltezhet az egsz programban. A kapcsolds nlkli azonost egyedi entits. Ha a blokkban az azonost deklarcija nem vonja maga utn az extern trolsi osztly specifiktort, akkor az azonostnak nincs kapcsoldsa, s egyedi a fggvnyre. A kvetkez azonostknak nincs kapcsoldsa: Brmely nem objektum vagy fggvnynvvel deklarlt azonostnak. Ilyen pldul a tpusdefincis (typedef) azonost. A fggvnyparamtereknek. Explicit extern trolsi osztly specifiktor nlkl deklarlt, blokk hatskr objektumazonostknak.

146

OBJEKTUMOK S FGGVNYEK

8.2 Fggvnyek A fggvnyekkel kapcsolatos alapfogalmakat tisztztuk mr a BEVEZETS S ALAPISMERETEK szakaszban, de fussunk t rajtuk mg egyszer! A fggvnynek kell legyen defincija, s lehetnek deklarcii. A fggvny defincija deklarcinak is minsl, ha megelzi a forrsszvegben a fggvnyhvst. A fggvnydefinciban van a fggvny teste, azaz az a kd, amit a fggvny meghvsakor vgrehajt a processzor. A fggvnydefinci rgzti a fggvny nevt, visszatrsi rtknek tpust, trolsi osztlyt s ms attribtumait. Ha a fggvnydefinciban a formlis paramterek tpust, sorrendjt s szmt is elrjk, fggvny prototpusnak nevezzk. A fggvny deklarcijnak meg kell elznie a fggvnyhvst, melyben aktulis paramterek vannak. Ez az oka annak, hogy a forrsfjlban a szabvny fggvnyek hvsa eltt behozzuk a prototpusaikat tartalmaz fejfjlokat (#include). A fggvnyparamtereket argumentumoknak is szoktk nevezni. A fggvnyeket a forrsfjlokban szoks definilni, vagy elrefordtott knyvtrakbl lehet bekapcsoltatni (linkage). Egy fggvny a programban tbbszr is deklarlhat, feltve, hogy a deklarcik kompatibilisek. A fggvny prototpusok hasznlata a C-ben ajnlatos (a C++ meg gy is ktelezen elrja), mert a fordtt gy ltjuk el elegend informcival ahhoz, hogy ellenrizhesse a fggvny nevt (a fggvnyek adott azonost), a paramterek szmt, tpust s sorrendjt (tpuskonverzi lehetsges), valamint a fggvny ltal visszaadott rtk tpust (tpuskonverzi itt is lehet). A fggvnyhvs truhzza a vezrlst a hv fggvnybl a hvott fggvnybe gy, hogy az aktulis paramtereket is ha vannak tadja rtk szerint. Ha a hvott fggvnyben return utastsra r a vgrehajts, akkor visszakapja a vezrlst a hv fggvny egy visszaadott rtkkel egytt (ha megadtak ilyet!). Egy fggvnyre a programban csak egyetlen definci lehetsges. A deklarcik (prototpusok) ktelesek egyezni a defincival.

C programnyelv

147

8.2.1 Fggvnydefinci A fggvnydefinci specifiklja a fggvny nevt, a formlis paramterek tpust, sorrendjt s szmt, valamint a visszatrsi rtk tpust, a fggvny trolsi osztlyt s ms attribtumait. A fggvnydefinciban van a fggvny teste is, azaz a hasznlatos loklis vltozk deklarcija, s a fggvny tevkenysgt megszab utastsok. A szintaktika:
fordtsi-egysg: kls-deklarci fordtsi-egysg kls-deklarci kls-deklarci: fggvnydefinci deklarci fggvnydefinci: <deklarci-specifiktorok> deklartor <deklarcilista> sszetett-utasts deklartor: <mutat> direkt-deklartor direkt-deklartor: direkt-deklartor(paramter-tpus-lista) direkt-deklartor(<azonostlista>) deklarcilista: deklarci deklarcilista deklarci

A kls-deklarcik hatskre a fordtsi egysg vgig tart. A klsdeklarci szintaktikja egyezik a tbbi deklarcival, de fggvnyeket csak ezen a szinten szabad definilni, azaz: tilos fggvnyben msik fggvnyt definilni! A deklarci s az azonostlista defincik a TPUSOK S KONSTANSOK szakasz Deklarci fejezetben megtallhatk. A mutatkat a kvetkez szakasz tartalmazza. A fggvnydefincibeli sszetett-utasts a fggvny teste, mely tartalmazza a hasznlatos loklis vltozk deklarciit, a klsleg deklarlt ttelekre val hivatkozsokat, s a fggvny tevkenysgt megvalst utastsokat. Az opcionlis deklarci-specifiktorok s a ktelezen megadand deklartor egytt rgztik a fggvny visszatrsi rtk tpust s nevt. A deklartor termszetesen fggvnydeklartor, azaz a fggvnynv s az t kvet zrjel pr. Az els direkt-deklartor(paramter-tpus-lista) alak a fggvny j (modern) stlus defincijt teszi lehetv. A deklartor szintaktikban szerepl direkt-deklartor a modern stlus szerint a definils alatt ll fggvny nevt rgzti, s a kerek zrjelben ll pa-

148

OBJEKTUMOK S FGGVNYEK

ramter-tpus-lista specifiklja az sszes paramter tpust. Ilyen deklartor tulajdonkppen a fggvny prototpus is. Pldul:
char fv(int i, double d){ /* . . . */ }

A msodik direkt-deklartor(<azonostlista>) forma a rgi stlus definci:


char fv(i, d) int i; double d; { /* . . . */ }

A tovbbiakban csak az j stlus fggvnydefincival foglalkozunk, s nem emlegetjk tovbb a rgit!


deklarci-specifiktorok: trolsi-osztly-specifiktor <deklarci-specifiktorok> tpusspecifiktor <deklarci-specifiktorok> tpusmdost <deklarci-specifiktorok> tpusmdost: (a kvetkezk egyike!) const volatile

A trolsi-osztly-specifiktorok s a tpuspecifiktorok defincii a TPUSOK S KONSTANSOK szakasz Deklarci fejezetben megtekinthetk! 8.2.1.1 Trolsi osztly Fggvnydefinciban kt trolsi osztly kulcssz hasznlhat: az extern vagy a static. A fggvnyek alaprtelmezs szerint extern trolsi osztlyak, azaz normlisan a program minden forrsfjljbl elrhetk, de explicit mdon is deklarlhatk externnek. Ha a fggvny deklarcija tartalmazza az extern trolsi osztly specifiktort, akkor az azonostnak ugyanaz a kapcsoldsa, mint brmely lthat, fjl hatskr ugyanilyen kls deklarcinak, s ugyanazt a fggvnyt jelenti. Ha nincs ilyen fjl hatskr, lthat deklarci, akkor az azonost kls kapcsolds. A fjl hatskr, trolsi osztly specifiktor nlkli azonost mindig kls kapcsolds. A kls kapcsolds azt jelenti, hogy az azonost minden pldnya ugyanarra a fggvnyre hivatkozik, azaz az explicit vagy implicit mdon extern trolsi osztly fggvny a program minden forrsfjljban lthat. Az externtl klnbz trolsi osztly, blokk hatskr fggvnydeklarci hibt generl.

C programnyelv

149

A fggvny explicit mdon deklarlhat azonban static-nek is, amikor is a r val hivatkozst az t tartalmaz forrsfjlra korltozzuk, azaz a fggvny bels kapcsolds, s csak a defincijt tartalmaz forrsmodulban lthat. Az ilyen fggvny legels bekvetkez deklarcijban (ha van ilyen!) s defincijban is ki kell rni a static kulcsszt. Akrmilyen esetrl is van sz azonban, a fggvny mindig a defincis vagy deklarcis pontjtl a forrsfjl vgig lthat magtl. 8.2.1.2 A visszatrsi rtk tpusa A visszatrsi rtk tpusa meghatrozza a fggvny ltal szolgltatott rtk mrett s tpust. A fggvnydefinci metanyelvi meghatrozsa az elhagyhat deklarci-specifiktorokkal kezddik. Ezek kzl tulajdonkppen a tpusspecifiktor felel meg a visszatrsi rtk tpusnak. E meghatrozsokat nzegetve lthat, hogy a visszaadott rtk tpusa brmi lehet eltekintve a tmbtl s a fggvnytl (az ezekre mutat mutat persze megengedett). Lehet valamilyen aritmetikai tpus, lehet void (nincs visszaadott rtk), de el is hagyhat, amikor is alaprtelmezs az int. Lehet struktra, uni vagy mutat is, melyekrl majd ksbbi szakaszokban lesz sz. A fggvnydefinciban elrt visszaadott rtk tpusnak egyeznie kell a programban brhol elfordul, e fggvnyre vonatkoz deklarcikban megadott visszatrsi rtk tpussal. A meghvott fggvny akkor ad viszsza rtket a hv fggvnynek a hvs pontjra, ha a processzor kifejezssel elltott return utastst hajt vgre. A fordt termszetesen elbb kirtkeli a kifejezst, s konvertlja ha szksges az rtket a visszaadott rtk tpusra. A void visszatrsnek deklarlt fggvnybeli kifejezssel elltott return figyelmeztet zenetet eredmnyez, s a fordt nem rtkeli ki a kifejezst. Vigyzat! A fggvny tpusa nem azonos a visszatrsi rtk tpusval. A fggvny tpusban ezen kvl benne van mg a paramterek szma, tpusai s sorrendje is! 8.2.1.3 Formlis paramterdeklarcik A fggvnydefinci metanyelvi meghatrozsbl kvetkezen a modern stlus direkt-deklartor(paramter-tpus-lista) alakban, a zrjelben

150

OBJEKTUMOK S FGGVNYEK

ll paramter-tpus-lista vesszvel elvlasztott paramterdeklarcik sorozata.


paramter-tpus-lista: paramterlista paramterlista, ... paramterlista: paramterdeklarci paramterlista, paramterdeklarci paramterdeklarci: deklarci-specifiktor deklartor deklarci-specifiktor <absztrakt-deklartor> absztrakt-deklartor: mutat <mutat><direkt-absztrakt-deklartor> direkt-absztrakt-deklartor: (absztrakt-deklartor) <direkt-absztrakt-deklartor>[<konstans-kifejezs>] <direkt-absztrakt-deklartor>(<paramter-tpus-lista>)

A paramterdeklarci nem tartalmazhat ms trolsi-osztly-specifiktort, mint a registert. A deklarci-specifiktor szintaktikabeli tpusspecifiktor elhagyhat, ha a tpus int, s egybknt megadjk a register trolsi osztly specifiktort. sszestve a formlis paramterlista egy elemnek formja a kvetkez:
<register> tpusspecifiktor <deklartor>

Az autonak deklarlt fggvnyparamter fordtsi hiba! A C szablyai szerint a paramter lehet brmilyen aritmetikai tpus. Lehet akr tmb is, de fggvny nem (az erre mutat mutat persze megengedett). A paramter lehet termszetesen struktra, uni vagy mutat is, melyekrl majd ksbbi szakaszokban lesz sz. A paramterlista lehet void is, ami nincs paramter jelents. A formlis paramterazonostk nem definilhatk t a fggvny testnek kls blokkjban, csak egy ebbe begyazott bels blokkban, azaz a formlis paramterek hatskre s lettartama a fggvnytest teljes legkls blokkja. Az egyetlen rjuk is leglisan alkalmazhat trolsi osztly specifiktor a register. Pldul:
int f1(register int i){/* ... */}/* Igny regiszteres paramter tadsra. */

A const s a volatile mdostk hasznlhatk a formlis paramter deklartorokkal. Pldul a


void f0(double p1, const char s[]){

C programnyelv
/* . . . */ s[0]=A;

151
/* Szintaktikai hiba. */}

constnak deklarlt formlis paramtere nem lehet balrtk a fggvny testben, mert hibazenetet okoz. Ha nincs tadand paramter, akkor a paramterlista helyre a definciban s a prototpusban a void kulcssz rand:
int f2(void){/* ... */} /* Nincs paramter. */

Ha van legalbb egy formlis paramter a listban, akkor az ,...-ra is vgzdhet:


int f3(char str[], ...){/* ... */}/* Vltoz szm vagy tpus paramter. */

Az ilyen fggvny hvsban legalbb annyi aktulis paramtert meg kell adni, mint amennyi formlis paramter a ,... eltt van, de termszetesen ezeken tl tovbbi aktulis paramterek is elrhatk. A ,... eltti paramterek tpusnak s sorrendjnek ugyanannak kell lennie a fggvny deklarciiban (ha egyltaln vannak), mint a defincijban. A fggvny aktulis paramterei tpusnak az esetleges szoksos konverzi utn hozzrendels kompatibilisnek kell lennie a megfelel formlis paramter tpusokra. A ,... helyn ll aktulis paramtereket nem ellenrzi a fordt. Az STGARG.H fejfjlban vannak olyan makrk, melyek segtik a felhasznli, vltoz szm paramteres fggvnyek megalkotst! A tmra visszatrnk mg a MUTATK kapcsn! 8.2.1.4 A fggvny teste A fggvny teste elhagyhat deklarcis s vgrehajthat utastsokbl ll sszetett utasts, azaz az a kd, amit a fggvny meghvsakor vgrehajt a processzor.
sszetett-utasts: {<deklarcilista> <utastslista>}

A fggvnytestben deklarlt vltozk loklisak, auto trolsi osztlyak, ha msknt nem specifikltk ket. Ezek a loklis vltozk akkor jnnek ltre, mikor a fggvnyt meghvjk, s loklis inicializlst hajt rajtuk vgre a fordt. A fggvny meghvsakor a vezrlst a fggvnytest els vgrehajthat utastsa kapja meg. void-ot visszaad fggvny blokkjban aztn a vgrehajts addig folytatdik, mg return utasts nem kvetkezik vagy a fggvny blokkjt zr }-re nem kerl a vezrls. Ezutn a hvsi ponttl folytatdik a program vgrehajtsa.

152

OBJEKTUMOK S FGGVNYEK

A valamit szolgltat fggvnyben viszont lennie kell legalbb egy return kifejezs utastsnak, s visszatrs eltt r is kell, hogy kerljn a vezrls. A visszaadott rtk meghatrozatlan, ha a processzor nem hajt vgre return utastst, vagy a return utastshoz nem tartozott kifejezs. A kifejezs rtkt szksges esetben hozzrendelsi konverzinak veti al a fordt, ha a visszaadand rtk tpusa eltr a kifejezstl. 8.2.2 Fggvny prototpusok A fggvnydeklarci megelzi a defincit, s specifiklja a fggvny nevt, a visszatrsi rtk tpust, trolsi osztlyt s a fggvny ms attribtumait. A fggvnydeklarci akkor vlik prototpuss, ha benne megadjk az elvrt paramterek tpust, sorrendjt s szmt is. sszegezve: a fggvny prototpus csak abban klnbzik a defincitl, hogy a fggvny teste helyn egy ; van. Cben ugyan nem ktelez, de tegyk magunknak ktelezv a fggvny prototpus hasznlatt, mert ez a kvetkezket rgzti: A fggvny inttl klnbz visszatrsi rtk tpust. Ezt az informcit a fordt a fggvnyhvsok paramtertpus s szm megfeleltets ellenrzsn tl konverzik elvgzsre is felhasznlja. A paramterek konvertlt tpusa hatrozza meg azokat az aktulis paramter rtkeket, melyek msolatait a fggvnyhvs teszi ki a verembe. Gondoljuk csak meg, hogyha az intknt kirakott aktulis paramter rtket a fggvny doublenek tekinten, akkor nem csak e paramter flrertelmezsrl van sz, hanem az sszes tbbi ezt kvet is "elcsszik"! A prototpussal a fordt nem csak a visszatrsi rtk s a paramterek tpusegyezst tudja ellenrizni, hanem az attribtumokat is. Pldul a static trolsi osztly prototpus hatsra a fggvnydefincinak is ilyennek kell lennie. A fggvnydefinci mdostinak egyeznie kell a fggvnydeklarcik mdostival! A prototpusbeli azonost hatskre a prototpus. Prototpus adhat vltoz szm paramterre, ill. akkor is, ha paramter egyltaln nincs. A komplett paramterdeklarcik (int a) vegythetk az absztraktdeklartorokkal (int) ugyanabban a deklarciban. Pldul:
int add(int a, int);

C programnyelv

153

A paramter tpusnak deklarlsakor szksg lehet az adattpus nevnek feltntetsre, mely a tpusnv segtsgvel rhet el. A tpusnv az objektum olyan deklarcija, melybl hinyzik az azonost. A metanyelvi lers:
tpusnv: tpusspecifiktor-lista<absztrakt-deklartor> absztrakt-deklartor: mutat <mutat><direkt-absztrakt-deklartor> direkt-absztrakt-deklartor: (absztrakt-deklartor) <direkt-absztrakt-deklartor>[<konstans-kifejezs>] <direkt-absztrakt-deklartor>(<paramter-tpus-lista>)

Az absztrakt-deklartorban mindig megllapthat az a hely, ahol az azonostnak lennie kellene, ha a konstrukci deklarcin belli deklartor lenne. A kvetkez tpusnevek jelentse: int, 10 elem int tmb s nem meghatrozott elemszm int tmb:
int, int [10], int []

Lssuk be, hogy a fggvny prototpus a kd dokumentlsra is j! Szinte rgtn tudunk mindent a fggvnyrl:
void strcopy(char cel[], char forras[]);

A
<tpus> fv(void);

olyan fggvny prototpusa, melynek nincsenek paramterei. Norml esetben a fggvny prototpus olyan fggvnyt deklarl, mely fix szm paramtert fogad. Lehetsg van azonban vltoz szm vagy tpus paramter tadsra is. Az ilyen fggvny prototpus paramterlistja ...-tal vgzdik:
<tpus> fv(int, long, ...);

A fixen megadott paramtereket fordtsi idben ellenrzi a fordt, s a vltoz szm vagy tpus paramtert viszont a fggvny hvsakor tpusellenrzs nlkl adja t a veremben. Az STGARG.H fejfjlban vannak olyan makrk, melyek segtik a felhasznli, vltoz szm paramteres fggvnyek megalkotst! A tmra visszatrnk mg a MUTATK kapcsn! Nzznk nhny plda prototpust!
int f(); /* int-et visszaad fggvny, melynek paramtereirl nincs informcink. */

154
int f1(void);

OBJEKTUMOK S FGGVNYEK

/* Olyan int-et szolgltat fggvny, melynek nincsenek paramterei. */ int f2(int, long); /* int-et visszaad fggvny, mely elsnek egy int, s aztn egy long paramtert fogad. */ int pascal f3(void);/*Paramter nlkli, int-et szolgltat pascal fggvny. */ int printf(const char [], ...);/* int-tel visszatr fggvny egy fix, s nem meghatrozott szm vagy tpus paramterrel. */

Ksztsnk programot, mely megllaptja az .HH.NN alak karakterlncrl, hogy rvnyes dtume!
/* PELDA21.C: Dtumellenrzs. */ #include <stdio.h> #include <stdlib.h> /* Az atoi miatt! */ #define INP 11 /* Az input puffer mrete. */ #include <ctype.h> /* Az isdigit miatt! */ int getline(char [], int); int datume(const char []); void main(void){ char s[INP+1]; /* Az input puffer. */ printf( "Dtumellenrzs.\nBefejezs res sorral!\n"); while( printf("\nDtum (.HH.NN)? "), getline(s,INP)>0) if(datume(s)) printf("rvnyes!\n"); else printf("rvnytelen!\n"); }

Az .HH.NN alak dtum rvnyessgt a krdsre logikai rtk vlaszt szolgltat, int datume(const char s[]) fggvny segtsgvel rdemes eldntetni. Kvetelmnyek: A karakterlncnak 10 hosszsgnak kell lennie. A hnapot az vtl elvlaszt karakter nem lehet numerikus, s azonosnak kell lennie a hnapot a naptl elvlasztval. A lncbeli sszes tbbi karakter csak numerikus lehet. Csak 0001 s 9999 kztti vet fogadunk el. Az vszm alapjn megllaptjuk a februr hnap napszmt. A hnapszm csak 01 s 12 kztti lehet. A napszm megengedett rtke 01 s a hnapszm maximlis napszma kztt van.
int datume(const char s[]){

C programnyelv

155

static int honap[ ] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; int i, ho; if(!s[10] && !isdigit(s[4]) && s[4]==s[7]){ for(i=0; i<10; ++i){ if(i==4||i==7) ++i; if(!isdigit(s[i])) return 0; } if((i=atoi(s))>=1){ honap[2]=28+(!(i%4)&&i%100 || !(i%400)); if((ho=10*(s[5]-'0')+s[6]-'0')>=1&&ho<=12&& (i=10*(s[8]-'0')+s[9]-'0')>=1&& i<=honap[ho]) return 1; } } return 0; }

Megoldand feladatok: Vltoztasson gy az int datume(const char s[]) fggvnyen, hogy akr egyjegy is lehessen: az vszm, majd a hnap s a napszm is! int indexe(char s[], char t[]) s int indexu(char s[], char t[]) fggvnyek ksztendk, melyek meghatrozzk s visszaadjk a t paramter karakterlnc s karaktertmbbeli els, illetve utols elfordulsnak indext! Prblja is ki egy rvid tesztprogrammal a fggvnyeket! rjon olyan szoftvert, mely megkeresi egy prba karakterlnc sszes elfordulst a szabvny bemenetrl rkez sorokban! 8.2.3 Fggvnyek hvsa s paramterkonverzik A fggvnyt aktulis paramterekkel hvjuk meg. Ezek sorrendjt s tpust a formlis paramterek hatrozzk meg. A fggvnyhvs opertor alakja
uttag-kifejezs(<kifejezslista>) kifejezslista: hozzrendels-kifejezs kifejezslista, hozzrendels-kifejezs

, ahol az uttag-kifejezs egy fggvny neve, vagy fggvnycmm rtkeli ki a fordt, s ezt hvja meg. A zrjelben ll, elhagyhat kifejezslista tagjait egymstl vessz vlasztja el, s tudjuk, hogy ezek azok az aktulis paramter kifejezsek, melyek rtkmsolatait a hvott fggvny kapja meg.

156

OBJEKTUMOK S FGGVNYEK

Ha az uttag-kifejezs nem deklarlt azonost az aktulis hatskrben, akkor a fordt implicit mdon a fggvnyhvs blokkjban
extern int azonost();

mdon tekinti deklarltnak. A fggvnyhvs kifejezs rtke s tpusa a fggvny visszatrsi rtke s tpusa. Az rtket vissza nem ad fggvnyt voidnak kell deklarlni, ill. void rand a kifejezslista helyre, ha a fggvnynek nincs paramtere. Ha a prototpus paramterlistja void, akkor a fordt zrus paramtert vr mind a fggvnyhvsban, mind a definciban. E szably megsrtse hibazenethez vezet. Az aktulis paramter kifejezslista kirtkelsi sorrendje nem meghatrozott, pontosabban a konkrt fordttl fgg. A ms paramter mellkhatstl fgg paramter rtke gy ugyancsak definilatlan. A fggvnyhvs opertor egyedl azt garantlja, hogy a fordt a paramterlista minden mellkhatst realizlja, mieltt a vezrlst a fggvnyre adn. Fggvnynek tmb s fggvny nem adhat t paramterknt, de ezekre mutat mutat persze igen. A paramter lehet aritmetikai tpus. Lehet struktra, uni vagy mutat is, de ezekkel ksbbi szakaszokban foglalkozunk. A paramter tadsa rtk szerinti, azaz a fggvny az rtkmsolatot kapja meg, melyet termszetesen el is ronthat a hvs helyn lev eredeti rtkre gyakorolt brmifle hats nlkl. Szval a fggvny mdosthatja a formlis paramterek rtkt. A fordt kirtkeli a fggvnyhvs kifejezslistjt, s szoksos konverzit (egszellptetst) hajt vgre minden aktulis paramteren. Ez azt jelenti, hogy a float rtkbl double lesz, a char s a short rtkbl int, valamint az unsigned char s az unsigned short rtkbl unsigned int vlik. Ha van vonatkoz deklarci a fggvnyhvs eltt, de nincs benne informci a paramterekre, akkor a fordt ksz az aktulis paramterek rtkvel. Ha deklarltak elzetesen fggvny prototpust, akkor az eredmny aktulis paramter tpust hasonltja a fordt a prototpusbeli megfelel paramter tpusval. Ha nem egyeznek, akkor a deklarlt formlis paramter tpusra alaktja az aktulis paramter rtkt hozzrendelsi kon-

C programnyelv

157

verzival, s jra a szoksos konverzi kvetkezik. A nem egyezs msik lehetsges vgkifejlete diagnosztikai zenet. A hvsnl a kifejezslistabeli paramterek szmnak egyeznie kell a fggvny prototpus vagy definci paramtereinek szmval. Kivtel az, ha a prototpus ,...-tal vgzdik, amikor is a fordt a fix paramtereket az elz pontban ismertetett mdon kezeli, s a ,... helyn lev aktulis paramtereket gy manipullja, mintha nem deklarltak volna fggvny prototpust. 8.2.4 Nem szabvnyos mdostk, hvsi konvenci A deklarci deklartorlistjban a megismert szabvnyos alaptpusokon, tpusmdostkon kvl minden fordtprogram rendelkezik mg specilis clokat szolgl, a deklarlt objektum tulajdonsgait vltoztat, nem szabvnyos mdostkkal is. Az olvasnak javasoljuk, hogy nzzen utna ezeknek a programfejleszt rendszere segtsgben! Teljessgre val trekvs nlkl felsorolunk itt nhny ilyen mdostt, melyek kzl egyikmsik ki is zrja egymst!
mdost: cdecl pascal interrupt fastcall stdcall export near far huge

Az els nhny mdost a fggvny hvsi konvencijt hatrozza meg. Az alaprtelmezett hvsi konvenci C programokra cdecl. Tekintsnk csak bele brmelyik szabvnyos fejfjlba, mindegyik fggvny prototpusa elejn ott talljuk a cdecl mdostt! Ha egy azonost esetben biztostani kvnjuk a kis-nagybet rzkenysget, az alhzs karakter (_) nv el kerlst, ill. fggvnynvnl a paramterek jobbrl balra val verembe rakst, akkor az azonost deklarcijban rjuk ki expliciten a cdecl mdostt! Ez a hvsi konvenci biztostja az igazi vltoz paramteres fggvnyek rst, hisz a vermet a hv fggvnynek kell rendbe tennie.

158

OBJEKTUMOK S FGGVNYEK

Vltoz paramteres fggvnyek rsval majd a MUTATK kapcsn foglalkozunk, most vegynk egy pldt az elmondottakra!
void fv(short s, int i, double d){} void main(void){ static short s=5; static int i=7; static float f=3.14f; /* . . . */ fv(s, i, f); /* . . . */ }

Hvs A paramter rtkeket jobbrl balra haladva rakja ki a verembe a kd a konverzik elvgzse utn, majd meghvja az fvt. A veremmutat SP!

A hvott fggvny

Verem helyrellts

Hozzfr az rtkm- A hv fggvnyben solatokhoz a verem- 16tal cskkenti a kd ben, de kzben az SP az SP rtkt. nem vltozik meg. SP: Elvgzi a dolgt a Vltoz szm parafggvny, s visszatr mter tadsa knnyen a hv fggvnybe. lehetsges, hiszen ha SP+12: 3.14 (double) SP+8: 3.14 (double) mg a hvs helyn sem SP+8: 7 (int) ismerjk az aktulis paSP+4: 7 (int) ramterek szmt, akkor SP+4: 5 (int) SP: 5 (int) nem fogjuk megtudni SP: visszatrsi cm sohasem. Az stdcall kisnagybet rzkeny. Standard hvsi konvencival kell hvni a WIN32 API fggvnyek tbbsgt. A fastcall mdost azt jelenti, hogy a fordt regiszterekben igyekszik tadni a paramtereket a hvott fggvnynek a verembe raks sorrendjben. Ha nincs elg regiszter, akkor a maradkot vermen t juttatja el a hvhoz. A visszatrsi rtk tadsa ugyancsak regiszteren t trtnik, ha lehetsges stb. A cdecl nagyobb vgrehajthat kdot generl, mint a fastcall, vagy az stdcall, hisz a hvsit, vermet rendbe tev kdnak kell kvetnie a hv fggvnyben. Mr mondottuk, hogy cdecl az alaprtelmezett hvsi konvenci C programokra. A mainnek mindenkpp cdecl-nek kell lennie, mert az indt kd C hvsi konvencik szerint hvja meg. A legtbb programfejleszt rendszerben bellthat az ltalnosan hasznland hvsi konvenci, mely mindig fellbrlhat a kvnt mdost explicit kirsval.

C programnyelv

159

Ha pldul nem cdecl hvsi sorrend programban szeretnnk hasznlni a printf fggvnyt, akkor:
extern cdecl printf(const char format[], ...); void egeszekki(int i, int j, int k); void cdecl main(void){ egeszekki( 1, 4, 9); } void egeszekki(int i, int j, int k){ printf(%d %d %d\n, i, j, k); }

mdon kell dolgozni. Elismerjk, hogy #include <stdio.h>-t alkalmazva semmi szksg sincs az
extern cdecl printf(const char format[], ...);

kirsra, hisz ez a prototpus amgy is benne van az STDIO.H fejfjlban. 8.2.5 Rekurzv fggvnyhvs Brmely fggvny meghvhatja nmagt kzvetlenl vagy kzvetve. A rekurzv fggvnyhvsoknak egyedl a verem mrete szab hatrt. A verem mrett befolysol, pldul kapcsol-szerkeszt opci, belltst megtudhatjuk programfejleszt rendszernk segtsgbl. Valahnyszor meghvjk a fggvnyt, j trol terletet allokl a rendszer az aktulis paramtereknek, az auto s a nem regiszterben trolt register vltozknak. A paramterek s a loklis vltozk teht a veremben jnnek ltre a fggvnybe val belpskor, s megsznnek, mihelyt a vezrls tvozik a fggvnybl, azaz: valahnyszor meghvjuk a fggvnyt, sajt loklis vltoz s aktulis paramter msolatokkal rendelkezik, ami biztostja, hogy a fggvny baj nlkl meghvhatja nmagt kzvetlenl vagy kzvetetten (ms fggvnyeken t). A rekurzvan hvott fggvnyek tulajdonkppen dolgozhatnak dinamikusan kezelt, globlis vagy static trolsi osztly loklis vltozkkal is. Azt azonban ilyenkor ne felejtsk el, hogy a fggvny sszes hvspldnya ugyanazt a vltozt ri el. Ha az lenne a feladatunk, hogy rjunk egy olyan fggvnyt, mely meghatrozza n faktorilist, akkor valsznleg gy jrnnk el:
long double faktor(int n){ long double N = n<1? 1.L: n; while(--n) N*=n; return N; }

160

OBJEKTUMOK S FGGVNYEK

Lthat, hogy a long double brzolsi formt vlasztottuk, hogy a lehet legnagyobb szm faktorilist legynk kpesek meghatrozni a C szmbrzolsi lehetsgeivel. Az algoritmus az egynl kisebb egszek faktorilist egynek tekinti, s az ismtelt szorzst fordtott sorrendben hajtja vgre, vagyis: N=n*(n1)*(n2)**3*2*1. rjunk egy rvid keretprogramot, mely bekri azt az 1 s MAXN (fordtsi idben vltoztathat) kzti egsz szmot, melynek megllapttatjuk a faktorilist!
/* PELDA22.C: Rekurzis pldaprogram: faktorilis. */ #include <stdio.h> #include <stdlib.h> #include <limits.h> #define MAX 40 /* Az input puffer mrete. */ #define MAXN 40 /* A max. szm. */ long double faktor(int n); int getline(char s[],int n); /* A decimlis szmjegyek szma maximlisan: */ #define HSZ sizeof(int)/sizeof(short)*5 int egesze(char s[]); void main(void){ int n=0; /* A szm. */ char sor[MAX+1]; /* A bemeneti puffer. */ printf("\n\t\tEgsz szm faktorilisa.\n"); while(n<1||n>MAXN){ printf("\nMelyik egsz faktorilist szmtjuk (1-%d)? ",MAXN); getline(sor,MAX); if(egesze(sor)) n=atoi(sor); } printf("%d! = %-20.0Lf \n",n, faktor(n)); }

Ismeretes, hogyha a formtumspecifikciban elrjuk a mezszlessget, akkor a kijelzs alaprtelmezs szerint jobbra igaztott. A balra igazts a szlessg el rt jellel rhet el. A faktorilisszmts rekurzv megoldsa a kvetkez lehetne:
long double faktor(int n){ if(n <= 1) return 1.L; else return (n*faktor(n-1)); }

Lthat, hogy a faktor egynl nem nagyobb n paramter esetn azonnal long double 1gyel tr vissza. Ms esetben viszont meghvja nmagt nnl eggyel kisebb paramterrel. Az itt visszakapott rtket megszorozza nnel, s ez lesz a majdani visszaadott rtke. Nzzk meg asztali teszttel, hogyan trtnnek a hvsok, feltve, hogy a main a 0s hvsi szint faktor(5)tel indult!

C programnyelv n: Hvsi szint: Visszatrsi szint: Visszatrsi rtk: 5 1 0 120 4 2 1 24 3 3 2 6 2 4 3 2

161 1 5 4 1

Hvs: faktor(4) faktor(3) faktor(2) faktor(1)

Cserljk ki a keretprogramban a faktor fggvnyt a rekurzv vltozatra, s prbljuk ki! Megoldand feladatok: rja meg a kvetkez fggvnyek rekurzv vltozatai: void strrv(char s[]), mely megfordtja a sajt helyn a paramter karakterlncot. Az itoa fggvny, mely az int paramtert karakterlncc konvertlja, s elhelyezi az ugyancsak paramter karaktertmbben.

162

MUTATK

9 MUTATK
A nyelvben a mutatknak kt fajtja van: (adat) objektumra mutat mutatk s fggvnyre (kd) mutat mutatk. Brmilyenek is legyenek, a mutatk memria cmek trolsra szolglnak. Elbb az adatmutatkkal fogunk foglalkozni. A mutat mindaddig, mg errl kln nem szlunk, jelentsen adatmutatt! A tpus tpus mutat tpus tpus objektum cmt tartalmazhatja. A mutatk unsigned egszek, de sajt szablyaik s korltozsaik vannak a hozzrendelsre, a konverzira s az aritmetikra. Miutn a mutat is skalr objektum, ltezik a mutatra mutat mutat is. A mutatk azonban tbbnyire ms skalr vagy void objektumokra (vltozkra), ill. aggregtumokra (tmbkre, struktrkra s unikra) mutatnak. 9.1 Mutatdeklarcik A mutatdeklarci elnevezi a mutat vltozt, s rgzti annak az objektumnak a tpust, melyre ez a vltoz mutathat. Teht csak egy bizonyos tpusra (elredefiniltra - belertve a void-ot is - vagy felhasznl definiltra) mutat mutat deklarlhat
tpus *azonost;

mdon, amikor is az azonost nev mutat tpus tpus objektum cmt veheti fel. Vegyk szre, hogy tpus * az azonost tpusa! Pldul az
int *imut; int *fv(char *);

deklarcik alapjn: imut int tpus objektumra mutat mutat, fv int tpus cmet visszaad, s egy char objektumra mutat paramtert fogad fggvny. Ha a definilt mutat statikus lettartam, akkor tiszta zrus kezdrtket kap implicit mdon. A fordt tiszta zrus cmen nem helyez el sem objektumot, sem fggvnyt, s gy a zrus cm (az n. NULL mutat) specilis felhasznls. Magt a NULL mutatt a szabvnyos fejfjlokban (pldul az STDIO.Hban) definilja a nyelv.

C programnyelv

163

Brmilyen tpus mutat NULL-hoz hasonltsa mindenkor helyes eredmnyre vezet, ill. brmilyen tpus mutathoz hozzrendelhetjk a NULL mutatt. Ha loklis a definilt mutat, akkor a definci hatsra a fordt akkora memriaterletet foglal, hogy abban egy cm elfrjen, de a lefoglalt bjtok szemetet tartalmaznak. Az ilyen mutat teht nem hasznlhat mindaddig rtelmesen semmire, mg valamilyen mdon rvnyes cmet nem tesznk bele. Hogy lehet elrni valamilyen objektum cmt? 9.1.1 Cm opertor (&) Egyoperandusos, magas priorits mvelet, mely definci szerint:
& eltag-kifejezs

alak. Az eltag-kifejezs operandusnak vagy fggvnyt kell kijellnie, vagy olyan objektumot elr balrtknek kell lennie, mely nem register trolsi osztly, s nem bitmez. Lehet teht pldul vltoz, vagy tmbelem. A bitmezkkel a struktrk kapcsn ksbb foglalkozunk! Ha az operandus tpusa tpus, akkor az eredmny mutat a tpus tpusra. Cm csak mutatnak adhat t. A lehetsges mdszerek szerint vagy hozzrendeljk, vagy inicializtort alkalmazunk a deklarciban. Pldul:
tpus val1, val2, *ptr = &val1; /* A ptr mutatnak van kezdrtke, de a vele elrt objektumnak nincs. */ ptr = &val2; /* Hozzrendeljk a msik vltoz cmt a mutathoz. */

Ha
T1 *ptr1; T2 *ptr2;

klnbz tpus objektumokra mutat mutatk, akkor a


ptr1 = ptr2;

vagy a
ptr2 = ptr1;

hozzrendels figyelmeztet vagy hibazenetet okoz. Explicit tpusmdost szerkezetet alkalmazva viszont gond nlkl mehet a dolog:
ptr1 = (T1 *) ptr2; ptr2 = (T2 *) ptr1;

164

MUTATK

Teljesen illeglis dolog azonban a fggvny s az adatmutatk szszerendelse! Ha a mutat mr rvnyes cmet tartalmaz, az 9.1.2 Indirekci opertor (*) segtsgvel elrhetjk a mutatott rtket. Az indirekci opertor ugyancsak egyoperandusos, magas priorits mvelet, melynek defincija:
* eltag-kifejezs

Az eltag-kifejezs operandusnak tpus tpusra mutat mutatnak kell lennie, ahol a tpus brmilyen lehet. Az indirekci eredmnye az eltagkifejezs mutatta cmen lev, tpus tpus rtk. Az indirekci eredmnye egyben balrtk is. Pldul:
tpus t1, t2; /* . . . */ tpus *ptr = &t1; /* A mutatt inicializltuk is. */ *ptr = t2; /* Ekvivalens a t1 = t2-vel. */

Ne ksreljk meg azonban a cm opertorral kifejezs vagy konstans cmt ellltani, vagy indirekci opertort nem cmm kirtkelhet operandus el odarni,
ptr = &(t1 + 6); ptr = &8; t2 = *t1; /* HIBS */ /* HIBS */ /* HIBS */

mert hibazenethez jutunk! Meghatrozatlan az indirekci eredmnye akkor is, ha a mutat: NULL mutat, vagy olyan loklis objektum cmt tartalmazza, mely a program adott pontjn nem lthat, vagy a vgrehajthat program ltal nem hasznlhat cmet tartalmaz. A mutatkkal vgzett munka sorn kl-szablyunk szerint: ahol objektum lehet egy kifejezsben, ott kerek zrjelbe tve az indirekci mvelett kveten ilyen tpus objektumra mutat mutat is llhat. Ha:
tpus val, *ptr = &val;

, akkor ahol val szerepelhet egy kifejezsben, ott llhat (*ptr) is.

C programnyelv Pldul:

165

int y, val=0, *ptr = &val; /* . . . */ printf("val cme (%p) van ptr-ben (%p).\n", &val, ptr); y = *ptr + 3; /* y = val +3 tulajdonkppen. */ y = sqrt((double) ptr); /* y = sqrt(val). */ *ptr += 6; /* val += 6. */ (*ptr)++; /* Zrjel nlkl a ptr-t inkrementltatnnk. */ printf("val rtke %d.\n", val); printf("A ptr cmen lev rtk %d.\n", *ptr); }

Fedezzk fel, hogy a cm, vagy mutattartalom kijelzshez hasznlatos tpuskarakter a p a formtumspecifikciban, s hogy a printf paramterlistjban cm rtk kifejezs is llhat, persze akr indirekcival is! Vigyzzunk nagyon! Ha egy vltoznak nem adunk kezdrtket, s mondjuk, hozzadogatjuk egy tmb elemeit, akkor a vgeredmny zldsg, s a dolog szarvashiba. Ha mutatnak nem adunk kezdrtket, s a benne lev szemtre, mint cmre, runk ki rtket indirekcival, akkor az dupln szarvashiba. A szemttel, mint cmmel valahol pancsolunk a memriban, s fellrjuk valami egszen ms objektum rtkt, s a hiba is egszen ms helyen jelentkezik, mint ahol elkvettk. 9.1.3 void mutat Kln kell emltennk a
void *vptr;

mutat tpust, ami nem semmire, hanem meghatrozatlan tpusra mutat mutat. Explicit tpusmdost szerkezet alkalmazsa nlkl brmilyen tpus mutat vagy cm hozzrendelhet a void mutathoz. A dolog megfordtva is igaz, azaz brmilyen tpus mutathoz hozzrendelhetnk void mutatt. Pldul:
tpus ertek, *ptr=&ertek; vptr=ptr; /* OK */ vptr=&ertek; /* OK */ ptr=vptr; /* OK */

void mutatval egyetlen mvelet nem vgezhet csak: az indirekci, hisz meghatrozatlan a tpus, s a fordt nem tudja, hogy hny bjtot s milyen rtelmezsben kell elrnie.
ertek=*vptr; /* HIBS */

166

MUTATK

9.1.4 Statikus s loklis cmek Miutn a statikus lettartam objektum minden bitjt zrusra inicializlja alaprtelmezs szerint a fordt, de cme nem vltozik, kezdrtke lehet akr statikus mutatnak is. Az auto vltozk cme viszont nem lehet statikus inicializtor, hisz a cm msms lehet a blokk klnbz vgrehajtsakor.
int GLOBALIS; int fv(void){ int LOKALIS; static int *slp = &LOKALIS; /* HIBS. */ static int *sgp = &GLOBALIS; /* OK. */ register int *rlp = &LOKALIS;/* OK. */ long a = 1000000l, *lp = &a;/* a kezdrtke 1000000 s lp kezdetben r mutat. */ register int *pi = 0; /* pi regiszteres mutat rtke NULL. */ const int i = 26; /* Ez az egyetlen hely, ahol i rtket kaphatott. */ /* . . . */ }

9.1.5

Mutatdeklartorok

deklartor: <mutat>direkt-deklartor direkt-deklartor: azonost (deklartor) direkt-deklartor [<konstans-kifejezs>] direkt-deklartor (paramter-tpus-lista) direkt-deklartor (<azonostlista>) mutat: *<tpusmdost-lista> *<tpusmdost-lista>mutat tpusmdost-lista: tpusmdost tpusmdost-lista tpusmdost tpusmdost: (a kvetkezk egyike!) const volatile

A deklartorok szerkezetileg az indirekcihoz, a fggvnyhez s a tmbkifejezshez hasonltanak, s csoportostsuk is azonos. A deklartort kvetheti egyenlsgjel utn inicializtor, de mindig deklarcispecifiktorok elzik meg. A deklarci-specifiktorok trolsi-osztlyspecifiktor s tpusspecifiktor sorozat tulajdonkpp, s igazbl nem csak egyetlen deklartorra vonatkoznak, hanem ezek listjra.

C programnyelv

167

Megkrjk az olvast, hogy lapozzon vissza egy pillanatra a TPUSOK S KONSTANSOK Deklarci fejezethez! A direkt-deklartor definci els lehetsge szerint a deklartor egy egyedi azonostt deklarl, melyre a trolsi osztly egy az egyben vonatkozik, de a tpus rtelmezse kicsit fgghet a deklartor alakjtl is. A deklartor teht egyedi azonostt hatroz meg, s mikor az azonost feltnik egy tle tpusban nem eltr kifejezsben, akkor a vele elnevezett objektum rtkt eredmnyezi. sszestve, s csak a lnyeget tekintve a deklarci
tpus deklartor

alak. Ezt nem vltoztatja meg az sem, ha a direkt-deklartor msodik alternatvjt tekintjk, mert a zrjelezs nem mdostja a tpust, csak sszetettebb deklartorok ktsre lehet hatssal. A fggvnydeklartorokat mr trgyaltuk, s a tmbdeklartorokra mg ebben a szakaszban visszatrnk! A mutatdeklarci gy mdosul:
tpus * <tpusmdost-lista> deklartor

, ahol a * <tpusmdost-list>val vltoztatott tpus a deklartor tpusa. A * opertor utn ll tpusmdost magra a mutatra, s nem a vele megcmezhet objektumra vonatkozik. Foglalkozzunk pldul a const mdostval! 9.1.6 Konstans mutat Mind a mutat, mind a mutatott objektum deklarlhat const-nak. Brmely const-nak deklarlt valami ugyebr nem vltoztathatja meg az rtkt. Az sem mehet persze, hogy olyan mutatt kreljunk, mellyel megsrthetnnk a const objektum rtk megvltoztathatatlansgt.
int i; int *pi; int * const const const /* pi int objektumra mutat inicializlatlan mutat. */ const cp = &i; /* cp konstans mutat int-re, de amire mutat, az nem konstans. */ int ci = 7; /* ci 7 rtk konstans int. */ int *pci; /* pci konstans int-re mutat. A mutat teht nem konstans. */ int * const cpc = &ci; /* cpc konstans int-re mutat konstans mutat. */

A kvetkez hozzrendelsek leglisak:

168

MUTATK

i = ci; /* const int hozzrendelse int-hez. */ *cp = ci;/* const int hozzrendelse konstans mutat mutatta nem konstans int-hez. */ ++pci; /* const int-re mutat mutat inkrementlsa.*/ pci = cpc;/*Konstans int-re mutat, konstans mutat hozzrendelse const int-re mutat mutathoz.*/

A kvetkez hozzrendelsek illeglisak:


ci = 0; /* rtkhozzrendelsi ksrlet const int-hez.*/ ci--; /* const int dekrementlsi ksrlete. */ *pci = 3;/* rtkadsi ksrlet a const int-re mutat mutatval megcmzett objektumnak. */ cp = &ci;/* rtkadsi ksrlet konstans mutatnak. */ cpc++; /* Konstans mutat inkrementlsi ksrlete. */ pi = pci;/* Ha ez a hozzrendels leglis lenne, akkor *pi = ... mdon mdosthatnnk azt a const int-et, amire pci mutat. */

Ahhoz, hogy a kgy megharapja a farkt kell, hogy const objektumra mutat mutatt ne lehessen hozzrendelni nem const-ra mutat mutathoz. Ha ez menne, akkor ugyan kerl ton, de a mutatott const rtk megvltoztathat lenne. 9.2 Mutatk s fggvnyparamterek Eddig kiemelten csak az n. rtk szerinti paramter tadssal foglalkoztunk a fggvnyhvs kapcsn. Ezt gy interpretlhatjuk, hogy a fordt fggvnyhvskor az aktulis paramterek (esetleg tpuskonverzin tesett) rtkt helyezi el, pldul a veremben, s a meghvott fggvny nem az aktulis paramterek rtkhez, hanem annak csak egy msolathoz fr hozz. Rvidsge s tallsga miatt tvesszk [4] vonatkoz mintapldjt! Tegyk fel, hogy a programoz azt a feladatot kapta, hogy rjon olyan fggvnyt, mely megcserli kt, int paramtere rtkt!
csere(paramter1, paramter2);

mdon hvhat els ksrlete a kvetkez volt:


void csere(int x, int y){ int seged = x; x = y; y = seged; }

Bartunk prblkozsa professzionlis olyan rtelemben, hogy gondolt arra, hogy a csere vgrehajtshoz szksge van segdvltozra, de a

C programnyelv

169

fggvnyt hv programjban meglepetten tapasztalta, hogy semmifle rtkcsere nem trtnt. A csere tulajdonkppen lezajlott az aktulis paramterek msolatain a veremben, de ennek semmilyen hatsa sincs az aktulis paramterek hv programbeli rtkeire. A fggvny visszatrse miatt a veremmutat is visszallt a hvs eltti rtkre, s gy a verembeli, felcserlt rtkmsolatok is elrhetetlenn vltak. A megolds a cm szerinti paramter tadsban rejlik, azaz a csere fggvnyt
csere(&paramter1, &paramter2);

mdon kell meghvni, s a fggvnydefinci pedig gy mdosul:


void csere(int *x, int *y){ int seged = *x; *x = *y; *y = seged; }

A rutin most az aktulis paramterek cmmsolatait kapja meg a veremben, s az indirekci mvelett alkalmazva gy az aktulis paramterek rtkn dolgozik. Megoldand feladatok: rja t a kvetkez, formai rvnyessget ellenrz fggvnyeket gy, hogy igaz (1) visszatrsi rtk mellett a vizsglt karakterlnc konvertlt eredmnyt is szolgltassk! Ha az rvnyessgellenrzs viszont hibval zrul, akkor a visszaadott hamis (0) rtken kvl semmifle rtkvltozst nem okozhat a fggvny. A PELDA18.Cben definilt egesze legyen int egesze(const char s[], int *ertek) prototpus! A PELDA20.Cbeli lebege talaktand int lebege(const char s[], double *ertek)k! A PELDA21.C datume fggvnybl vljk int datume(const char s[], int *ev, int *ho, int *nap)! Tmbk s mutatk A BEVEZETS S ALAPISMERETEK szakaszban kln fejezet foglalkozik a tmbkkel, s az Inicializls rsz trgyalja az egydimenzis tmbk kezdrtk adst is. Tmb ltesthet aritmetikai tpusokbl, de definilhat 9.3

170 mutatbl (kln ksbbi fejezet),

MUTATK

struktrbl s unibl (a kvetkez szakasz), valamint tmbbl (nll fejezete van a tbbdimenzis tmbknek). Brmilyen tpusbl is hozzuk azonban ltre a(z egydimenzis) tmbt, a tpusnak teljesnek kell lennie. Nem lehet flig ksz, nem teljesen definilt, felhasznli tpusbl tmbt krelni. A tmbk s a mutatk kztt nagyon szoros kapcsolat van. Ha kifejezs, vagy annak rsze tpus tmbje, akkor a (rsz)kifejezs rtkt a tmb els elemt megcmz, konstans mutatv alaktja a fordt, s a (rsz)kifejezs tpusa tpus * const lesz. Nem hajtja vgre a fordt ezt a konverzit, ha a (rsz)kifejezs cm opertor (&), ++, , vagy a pont (.) szelekcis opertor, vagy a sizeof operandusa, vagy hozzrendelsi mvelet bal oldaln ll. A . opertorral a kvetkez szakasz foglalkozik. Elemezzk az elz bekezdsben mondottakat egy plda tkrben! Legyen a kvetkez tmb s mutat!
#define MERET 20 /* Tmbmret. */ /* . . . */ float tomb[MERET], *pt; /* . . . */

A tmbt a fordt, mondjuk, a 100as cmtl kezdve helyezte el a memriban. Egy tmbelem helyfoglalsa sizeof(tomb[0]) sizeof(float) 4, ltalnossgban sizeof(tpus). Az elemek
tomb[0], tomb[1], ..., tomb[MERET - 1]

sorrendben, nvekv cmeken helyezkednek el a trban. Teht a tomb[0] a 100as, a tomb[1] a 104es, a tomb[2] a 108as s gy tovbb cmen van. A 180as memriacm mr nem tartozik a tmbhz. Ha valamilyen kifejezsben megltja a fordt a tomb azonostt, akkor rgtn helyettesteni fogja a float * const 100as cmmel. Teht, ha a ptt fel kvnjuk tlteni tomb kezdcmvel, akkor nem kell ilyen
pt = &tomb[0];

hosszadalmasan kdolni, tkletesen elg a


pt = tomb;

C programnyelv

171

9.3.1 Index opertor A MVELETEK S KIFEJEZSEK szakasz elejn definilt uttagkifejezs msodik alternatvja az
uttag-kifejezs[kifejezs]

az indexel opertor. Nevezik ezt indexes vltoznak is, vagyis mindenkppen hivatkozs ez a tmb egy meghatrozott elemre. A szablyok szerint az uttag-kifejezs s a kifejezs kzl az egyiknek mutatnak, a msiknak egsz tpusnak kell lennie, s hatsukra a fordt a
*((uttag-kifejezs) + (kifejezs))

mveletet valstja meg. A fordt alkalmazza a kvetkez fejezetben trgyalt konverzis szablyokat a + mveletre s a tmbre. Ha az uttagkifejezs a tmb s a kifejezs az egsz tpus, akkor a konstrukci a tmb kifejezsedik elemre hivatkozik. Mi van a tpussal? A kls zrjel prban mg mutat tpus (tpus *) van, s ezen hajtja vgre a fordt az indirekcit. Teht az eredmny tpust a mutat dnti el. Vegyk a tomb[6] indexes vltozt! A mondottak szerint ebbl
*((tomb) + (6))

lesz, ami nem 6, hanem 6nak, mint indexnek, a hozzadst jelenti a tomb kezdcmhez, azaz:
*((float *)100 + 6*sizeof(float))

Az sszeads elvgzse utn


*((float *)124)

, ami az indirekci vgrehajtsa utn a tomb 6os index elemt eredmnyezi. A + kommutatv mvelet, gy az indexels is az. Egydimenzis tmbkre a kvetkez ngy kifejezs teljesen ekvivalens feltve, hogy p mutat s i egsz:
p[i] i[p] *(p + i) *(i + p)

Folytassuk az index opertor ismertetsnek megkezdse eltt elkezdett gondolatmenetet, azaz a pt mutat legyen tomb rtk!
pt = tomb;

Ilyenkor:

172
*pt tomb[0], *(pt + 1) tomb[1]

MUTATK

s ltalban
*(pt + i) tomb[i].

Vegyk szre, hogy a pt-re szksg sincs a tmbelemek s cmeik ellltshoz, hisz:
tomb[i] *(tomb + i)

s
&tomb[i] tomb + i.

Fontos klnbsg van azonban a tomb s a pt kztt. A pt mutat vltoz. A tomb pedig mutat konstans. Ebbl kvetkezleg nem megengedettek a kvetkezk:
tomb = pt; tomb++; pt = &tomb; /* Mintha 3=i-t rtunk volna fel. */ /* Mint 3++. */ /* Mintha &3-at akarnnk ellltani. */

A mutat vltozra termszetesen megengedettek ezek a mveletek:


pt = tomb; pt++;

Ha pt mutat, akkor azt kifejezs indexelheti a tanultak szerint, azaz


pt[i] *(pt + i)

Meg kell emlteni mg, hogy amikor egy fggvnyt tmbazonost aktulis paramterrel hvtunk meg, akkor is cm szerinti paramter tads trtnt, azaz a fggvny a tmb kezdcm konstans msolatt kapta meg, pldul, a veremben. Ez a cmmsolat aztn persze a fggvnyben mr nem konstans, el is lehet rontani stb. Vegyk szre, hogy a cm szerinti paramter tadst szinte minden pldnkban, mr a kezdetek ta hasznljuk! Eddig a fggvnyparamter tmbt mindig
tpus azonost[]

alakban adtuk meg, de legjabb ismereteink szerint a


tpus * azonost

forma hasznland, hisz ez egyrtelmen mutatja, hogy a paramter mutat. A fggvny testn bell ettl fggetlenl szabadon dnthet a programoz, hogy tmbknt,

C programnyelv mutatknt, vagy vegyesen kezeli az ilyen paramtert.

173

A gpi kd utastsok operandusai tbbnyire memria cmek. Ebbl kvetkezleg minl inkbb mutatkat hasznlva rjuk meg programjainkat, annl kzelebb kerlnk a gpi kdhoz, s ez ltal annl gyorsabb lesz a szoftvernk. rjuk t ennek szellemben a PELDA21.C datume fggvnyt!
int datume(const char *s){ static int honap[ ] = { 0,31,28,31,30,31,30,31,31,30,31,30,31 }; int i, ho; if(!*(s+10) && !isdigit(*(s+4)) && *(s+4)==*(s+7)){ for(i=0; i<10; ++i){ if(i==4||i==7) ++i; if(!isdigit(*(s+i))) return 0; } if((i=atoi(s))>=1){ *(honap+2)=28+(!(i%4)&&i%100 || !(i%400)); if((ho=atoi(&s[5])>=1&&ho<=12&&(i=atoi(&s[8])>=1&& i<=*(honap+ho)) return 1; } } return 0; }

Lssuk be, hogy az ilyen fajta fggvny trsnak, amikor s[i]bl *(s+i)t csinlgatunk, semmi rtelme sincs, hisz ezt a fordt magtl is megteszi. Az talakts egyetlen elnye, hogy a formlis paramter jobban szemllteti, hogy const karakterlncra mutat, ill. az szre vehet, hogy az atoi fggvnyt nem csak a karaktertmb kezdetvel szabad meghvni. Foglalkozzunk mg egy kicsit a tmbdeklartorokkal! 9.3.2 Tmbdeklartor s nem teljes tpus tmb A Mutatdeklartorok fejezetben a direkt-deklartor definci harmadik vltozata
direkt-deklartor [<konstans-kifejezs>]

a tmbdeklartor. A tmbdeklarci teht


tpus deklartor [<konstans-kifejezs>]<={inicializtorlista<,>}>

, ahol az elhagyhat konstans-kifejezsnek egsz tpusnak s zrusnl nagyobb rtknek kell lennie, s ez a tmb mrete. Ez a TPUSOK S KONSTANSOK szakasz Deklarci fejezetben rottakon tl tovbbi korltozsokat r a konstans kifejezsre, hogy egsz tpusnak kell lennie.

174

MUTATK

Operandusai ebben az esetben csak egsz, felsorols, karakteres s lebegpontos llandk lehetnek, de a lebegpontos konstanst explicit tpuskonverzival egssz kell alaktani. Operandus lehet mg a sizeof opertor is, aminek operandusra termszetesen nincsenek ilyen korltozsok. Tudjuk, hogyha elmarad a tmbmret, akkor a fordt az inicializtorlista elemszmnak megllaptsval rgzti azt, s a tpus gy vlik teljess. Megadott mret tmb esetben a lista inicializtorainak szma nem haladhatja meg a tmb elemeinek szmt. Ha az inicializtorok kevesebben vannak a tmbmretnl, akkor a magasabb index, fennmarad tmbelemek zrus kezdrtket kapnak. Tudjuk azt is, hogy tmb inicializtorai csak lland kifejezsek lehetnek. Ne felejtkezznk meg rla, hogy ugyan a karaktertmb inicializtorlistja karakterlnc konstans, de az elbb felsorolt korltozsok ugyangy vonatkoznak r is! Vagyis rgztett mret tmb esetn a karakterlnc hossza sem haladhatja meg a mretet. Ha a lnchossz s a tmbmret egyezik, nem lesz lnczr zrus a karaktertmb vgn. Ha a tmbdeklarcibl hinyzik a tmbmret s inicializtorlista sincs, akkor a deklarci nem teljes tpus tmbt hatroz meg. Lssuk a lehetsges eseteket! Ez csak elzetes deklarci s a tmb mrett majd egy ksbbi definci rgzti. Pldul:
float t[]; /* . . . */ float t[20];

Ez csak elzetes deklarci s a tmbmretet egy ksbbi inicializtorlists deklarci tisztzza. Pldul:
char lanc[]; /* . . . */ char lanc[]=Ne tudd ki!;

Termszetesen az egytt tmbmretes s inicializtorlists definil deklarci is megengedett! A globlis tmbt nem ebben a forrsmodulban definiltk, itt csak egyszeren a r val hivatkozs eltt deklarltk. Pldul:
extern double dtomb[];

Persze a dtomb mrett valamilyen mdon ebben a forrsmodulban is ismernnk kell! A tmb fggvny paramtere. Pldul:

C programnyelv
void fv(float t[], int n) { /* . . . */ }

175

A tmb mrett valahonnan a fggvnyben is tudni kne! Pldul gy, hogy tovbbi paramterknt tadjk neki. 9.4 Mutataritmetika s konverzi A mutat vagy cmaritmetika az inkrementlsra, a dekrementlsra, az sszeadsra, a kivonsra s az sszehasonltsra szortkozik. A tpus tpus objektumra mutat mutatn vgrehajtott aritmetikai mveletek automatikusan figyelembe veszik a tpus mrett, azaz az objektum trolsra elhasznlt bjtok szmt. A mutataritmetika ezen kvl felttelezi, hogy a mutat a tpus tpus objektumok tmbjre mutat, azaz pldul:= 6; int i
float ftomb[50], *fptr = ftomb;

esetn
fptr += i;

hatsra az fptr-beli cm
sizeof(float)*i

-vel (ltalnossgban sizeof(tpus)*egsz-szel) n, azaz a plda szerint ftomb[6]-ra mutat. Ha ptr1 a tpus tpus tmb msodik s ptr2 a tizedik elemre mutat, akkor a kt mutat klnbsge
ptr2 - ptr1 8.

Figyeljk meg, hogy a mutatkhoz indexrtket adunk hozz vagy vonunk ki belle s a mutatk klnbsge is indexrtk! Igazbl a kt mutat klnbsge ptrdiff_t tpus egsz indexklnbsg. A ptrdiff_t az STDDEF.H fejfjlban definilt, s tbbnyire signed int. 9.4.1 sszeads, kivons, inkrementls s dekrementls sszeads egyik operandusa lehet mutat, ha a msik operandus egsz tpus. Az eredmny ilyenkor a cmaritmetika szablyait kvet mutat, azaz olyan cm, mely egsz*sizeof(tpus)sal nagyobb, mint az eredeti. A tpus a mutat ltal mutatott nem void tpus. Kivonsnl az els operandus lehet valamilyen objektumra mutat mutat, ha ilyenkor a msodik egsz tpus. Az eredmny most is a mutataritmetika szablyait kvet mutat, azaz olyan cm, mely egsz*sizeof(tpus)sal kisebb, mint az eredeti. A tpus a mutat ltal mutatott nem void tpus.

176

MUTATK

Kivonsnl mindkt operandus lehet ugyanazon objektum rszeire mutat mutat. Az eredmny az elz kt pont szellemben a mutataritmetika szablyainak megfelel egsz (indexklnbsg). Az egsz tpusa a szabvny STDDEF.H fejfjlban definilt ptrdiff_t (signed int). Ha az operandus mutat, akkor az eggyel nvelsben (++) vagy cskkentsben () a cmaritmetika szablyai rvnyesek, azaz az eredmny mutat a kvetkez, vagy a megelz elemre fog mutatni. Ha p mutat a tmb utols elemre mutat, akkor a ++p mg leglis rtk: a tmb utols utni elemnek cme, de minden ezutn kvetkez mutatnvels definilatlan eredmnyre vezet. Hasonl problma van akkor is, ha p a tmb kezdetre mutat. Ilyenkor a mutatcskkents - mr a --p is - definilatlan eredmnyt okoz. Vegyk szre, hogy az egsz mutataritmetiknak csak akkor van rtelme, ha a mutatk egyazon tmb elemeire mutatnak! Minden nem tmbre alkalmazott cmaritmetikai mvelet eredmnye definilatlan. Ugyanez mondhat el akkor is, ha a tmbre alkalmazott mutataritmetikai mvelet eredmnye a tmb legels eleme el, vagy a legutols utnin tlra mutat. 9.4.2 Relcik A kvetkez operandus tpuskombincik hasznlhatk relcikban: Mindkt operandus lehet ugyanazon tpus objektumra mutat mutat, amikor is a kt objektum memria cmnek sszehasonltsrl lesz sz. A mutat sszehasonlts csak egyazon objektum rszeire definilt, s a mutatk nem tartalmazhatnak ezen objektumon kvlre irnyul cmet sem egyetlen kivtellel, tmb esetn megengedett az utols ltez elem utni cmre val hivatkozs. Ha a mutatk tmb elemekre hivatkoznak, akkor az sszehasonlts az indexek sszehasonltsval ekvivalens. A nagyobb index elem cme magasabb. Az egyenlsgi relcikat hasznlva a mutat hasonlthat a konstans 0 rtkhez, azaz a NULL mutathoz is. Csak az egyenlsgi relcik esetben a mutat hasonlthat a konstans, 0 rtk, egsz kifejezshez, vagy voidra mutat mutathoz. Ha mindkt mutat NULL mutat, akkor egyenlk.

C programnyelv

177

9.4.3 Feltteles kifejezs Ha a K1 ? K2 : K3ban K2, K3 egyikemsika mutat, akkor a K2 vagy K3 operandusok tpustl fgg konstrukci eredmnynek tpusa a kvetkez: Ha az egyik operandus valamilyen tpus objektumra mutat s a msik void mutat, akkor az eredmny (esetleges konverzi utn) ugyancsak void mutat. Ha az egyik operandus mutat, s a msik operandus 0 rtk konstans kifejezs, akkor az eredmny tpusa a mutat tpusa lesz. Mutatk tpusnak sszehasonltsakor a const vagy volatile tpusmdostk nem szignifiknsak, de az eredmny tpusa megrkli mindkt oldal mdostit. rjuk t a cmaritmetika alkalmazsval a PELDA20.Cben hasznlt DVEREM.Ct!
/* DVEREMUT.C: double verem push, pop s clear fggvnyekkel. */ #define MERET 128 /* A verem mrete. */ static double v[MERET]; /* A verem. */ static double *vmut=v; /* A veremmutat. */ int clear(void){ vmut=v; return MERET; } double push(double x){ if(vmut<v+MERET) return *vmut++=x; else return x+1.; } double pop(void){ if(vmut>v) return *--vmut; else return 0.; }

A vmut most valban veremmutat a double veremben (s nem a processzorban). v kezdrtkkel indul, s mindig a kvetkez szabad helyre irnyul. Ha kisebb, mint a veremhez mr nem tartoz cm (v+MERET), akkor a push kiteszi r a paramtert, s mellkhatsknt elbbre is lpteti eggyel a veremmutatt a kvetkez szabad helyre. A pop csak akkor olvas a verembl, ha van benne valami (vmut>v). Kiszedskor elbb vissza kell lltani a veremmutatt (az eltag ), s csak aztn rhet el indirekcival a legutoljra kitett rtk, s most ez lesz a kvetkez szabad hely is egyben a kvetkez push szmra.

178

MUTATK

9.4.4 Konverzi A fordt ltal automatikusan elvgzett, implicit konverzikat mr megismertk a cmaritmetika mveleteinl. Az explicit tpuskonverzis
(tpusnv) eltag-kifejezs

szerkezetben a (tpusnv) tbbnyire (tpus *) alak lesz, s ilyen tpus objektumra mutat mutatv konvertlja az eltag-kifejezs rtkt. Pldul:
char *lanc; int *ip; /* . . . */ lanc = (char *) ip;

Nullartk konstans, egsz kifejezs, vagy ilyen (void *)gal tpusmdostva konvertlhat explicit tpusmdostssal, hozzrendelssel vagy sszehasonltssal akrmilyen tpus mutatv. Ez NULL mutatt eredmnyez, mely megegyezik az ugyanilyen tpus NULL mutatval, de eltr brmely ms objektumra vagy fggvnyre mutat mutattl. Egy bizonyos tpus mutat konvertlhat ms tpus mutatv. Az eredmny azonban cmzs hibs lehet, ha nem megfelel trilleszts objektumot rne el. Csak azonos, vagy kisebb szigorsg trillesztsi felttelekkel br adattpus mutatjv konvertlhat az adott mutat, s onnt vissza. A trilleszts hardver sajtossg, s azt jelenti, hogy a processzor bizonyos tpus adatokat csak bizonyos hatron lev cmeken helyezhet el. A legkevsb megszort a char tpus szokott lenni. A short csak szhatron (2vel maradk nlkl oszthat cmen) kezddhet, a long viszont dupla szhatron (4gyel maradk nlkl oszthat cmen) helyezkedhet el, s gy tovbb. Felkrjk az olvast, hogy programfejleszt rendszere segtsgben felttlenl nzzen utna a konkrtumoknak! void mutat kszthet akrmilyen tpus mutatbl, s megfordtva korltozs s informciveszts nlkl. Ha az eredmnyt visszakonvertljuk az eredeti tpusra, akkor az eredeti mutatt lltjuk jra el. Ha ugyanolyan, de ms, const vagy volatile mdostj tpusra konvertlunk, akkor az eredmny ugyanaz a mutat a mdost ltal elidzett megszortsokkal. Ha a mdostt aztn elhagyjuk, akkor a tovbbi mveletek sorn az eredetileg az objektum deklarcijban szerepl const vagy volatile mdostk maradnak rvnyben.

C programnyelv

179

A mutat mindig konvertlhat a trolshoz elegenden nagy, egsz tpuss. A mutat mrete, s az talakt fggvny persze nem gpfggetlen. Lerunk egy, tbb fejleszt rendszerben is hasznlatos mutategsz s egszmutat konverzit. A mutategsz konverzi mdszere fgg a mutat s az egsz tpus mrettl, valamint a kvetkez szablyoktl: Ha a mutat mrete nem kisebb az egsz tpusnl, akkor a mutat rtk unsignedknt viselkedik azzal a megktssel, hogy nem konvertlhat lebegpontoss. Ha a mutat mrete kisebb az egsz tpusnl, akkor a mutatt elbb az egsszel megegyez mretv alaktja a fordt, s aztn konvertlja egssz. Az egszmutat talakts sem portbilis, de a kvetkez szablyok szerint mehet pldul: Ha az egsz tpus ugyanolyan mret, mint a mutat, akkor az egsz rtket unsignedknt mutatnak tekinti a fordt. Ha az egsz tpus mrete klnbzik a mutattl, akkor az egsz tpust az elz pontokban ismertetett mdon konvertlja elbb mutat mret, egsz tpusv, majd unsignedknt mutatnak tekinti. 9.5 Karaktermutatk Tudjuk, hogy a karakterlnc konstans karaktertmb tpus, s ebbl kvetkezleg mgtte ugyancsak egy cm konstans van, hisz pldul a
printf(Ez egy karakterlnc konstans.);

kitnen mkdik, holott a printf fggvny els paramtere const char * tpus. Ez a konstans mutat azonban a tmbtl eltren nem rendelkezik azonostval sem, teht ksbb nincs mdunk hivatkozni r. Ennek elkerlsre, azaz a cm konstans rtknek megrzsre, a kvetkez mdszerek ajnlhatk:
char *uzenet; uzenet = Ksz a kv!\n;

vagy
const char *uzenet = Ksz a kv!\n;

9.5.1 Karakterlnc kezel fggvnyek A rutinok prototpusai a szabvnyos STRING.H fejfjlban helyezkednek el. Egyik rszknek strrel, msik csoportjuknak memmel kezddik

180

MUTATK

a neve. Az str kezdetek karakterlncokkal (char *), mg a mem nevek memriaterletekkel bjtonknt haladva (void * s nincs felttlenl lnczr zrus a bjtsorozat vgn) foglalkoznak. A char *, vagy void * viszszaadott rtk fggvnyek mindig az eredmny lnc kezdcmt szolgltatjk. A memmovetl eltekintve, a tbbi rutin viselkedse definilatlan, ha egymst a memriban tfed karaktertmbkre hasznljk ket. Nhnyat teljessgre val trekvs nlkl felsorolunk kzlk! char *strcat(char *cel, const char *forras); char *strncat(char *cel, const char *forras, size_t n); A fggvnyek a cel karakterlnchoz fzik a forrast (strcat), vagy a forras legfeljebb els, n karaktert (strncat), s visszatrnek az egyestett cel karakterlnc cmvel. Nincs hibt jelz visszaadott rtk! Nincs tlcsorduls vizsglat a karakterlncok msolsakor s hozzfzsekor. A size_t tbbnyire az unsigned int tpusneve. rjuk csak meg a sajt strncat fggvnynket!
char *strncat(char *cel, const char *forras, size_t n){ char *seged = cel; /* Pozcionls a cel lezr \0 karakterre: */ while(*cel ) ++cel; /* forras cel vgre msolsa a zr \0-ig, vagy legfeljebb n karakterig: */ while(n-- && (*cel++ = *forras++)); /* Vissza az egyestett karakterlnc kezdcme: */ return seged; }

char *strchr(const char *string, int c); void *memchr(const void *string, int c, size_t n); A rutinok a c karaktert keresik stringben, ill. string els n bjtjban, s az els elforduls cmvel trnek vissza, ill. NULL mutatval, ha nincs is c a stringben, vagy az els n bjtjban. A lnczr zrus is lehet c paramter. Az char *strrchr(const char *string, int c); ugyanazt teszi, mint az strchr, csak c stringbeli utols elfordulsnak cmvel tr vissza, ill. NULL mutatval, ha nincs is c a stringben. int strcmp(const char *string1, const char *string2); int strncmp(const char *string1, const char *string2, size_t n);

C programnyelv int memcmp(const void *string1, const void *string2, size_t n);

181

A fggvnyek unsigned char tpus tmbkknt sszehasonltjk string1 s string2 karakterlncokat, s negatv rtket szolgltatnak, ha string1 < string2. Pozitv rtk jn, ha string1 > string2. Az egyenlsget viszont a visszaadott zrus jelzi. Az strncmp s a memcmp a hasonltst legfljebb az els n karakterig, ill. bjtig vgzik. A legtbb fejleszt rendszerben nem szabvnyos stricmp s strnicmp is szokott lenni, melyek nem kisnagybet rzkenyen hasonltjk ssze a karakterlncokat. A sajt strcmp:
int strcmp(const char *s1, const char *s2 ){ for( ; *s1 == *s2; ++s1, ++s2) if(!(*s1)) return 0; /* s1 == s2 */ return(*s1 - *s2); } /* s1 < s2 vagy s1 > s2 */

char *strcpy(char *cel, const char *forras); char *strncpy(char *cel, const char *forras, size_t n); void *memcpy(void *cel, const void *forras, size_t n); void *memmove(void *cel, const void *forras, size_t n); Az strcpy a forras karakterlncot msolja lnczr karaktervel egytt a cel karaktertmbbe, s visszatr a cel cmmel. Nincs hibt jelz visszatrsi rtk. Nincs tlcsorduls ellenrzs a karakterlncok msolsnl. Az strncpy a forras legfeljebb els n karaktert msolja. Ha a forras n karakternl rvidebb, akkor cel vgt \0zza n hosszig a rutin. Ha az n nem kisebb, mint a forras mrete, akkor nincs zrus a msolt karakterlnc vgn. A memcpy s a memmove mindenkppen n bjtot msolnak. Egyetlenknt a karakterlnc kezel fggvnyek kzl, a memmove akkor is biztostja az tlapol memriaterleteken lev, eredeti forras bjtok fellrs eltti tmsolst, ha a forras s a cel tfedn egymst. A sajt strcpy:
char *strcpy(char *cel, const char *forras ){ char *seged = cel; /* forras cel-ba msolsa lezr \0 karaktervel: */ while(*cel++ = *forras++); /* Vissza a msolat karakterlnc kezdcme: */ return seged; }

182 size_t strlen(const char *string);

MUTATK

A rutin a string karakterlnc karaktereinek szmval tr vissza a lnczrt nem beszmtva. Nincs hibt jelz visszaadott rtk! char *strpbrk(const char *string, const char *strCharSet); A fggvnyek az strCharSetbeli karakterek valamelyikt keresik a stringben, s visszaadjk az els elforduls cmt, ill. NULL mutatt, ha a kt paramternek kzs karaktere sincs. A keressbe nem rtendk bele a lnczr zrusok. A pldban szmokat keresnk az s karakterlncban.
#include <string.h> #include <stdio.h> void main(void){ char s[100] = "3 frfi s 2 fiu 5 disznt ettek.\n"; char *er = s; int i=1; while(er = strpbrk(er, "0123456789")) printf("%d: %s\n", i++, er++); }

A kimenet a kvetkez:
1: 3 frfi s 2 fiu 5 disznt ettek. 2: 2 fiu 5 disznt ettek. 3: 5 disznt ettek.

char *strset(char *string, int c); char *strnset(char *string, int c, size_t n); void *memset(void *string, int c, size_t n); A memset feltlti string els n bjtjt c karakterrel, s visszaadja string kezdcmt. A legtbb fejleszt rendszerben ltez, nem ANSI szabvnyos strset s strnset a string minden karaktert a lnczr zrus kivtelvel c karakterre lltjk t, s visszaadjk a string kezdcmt. Nincs hibt jelz visszatrsi rtk. Az strnset azonban a string legfeljebb els n karaktert inicializlja cre. size_t strspn(const char *string, const char *strCharSet); size_t strcspn(const char *string, const char *strCharSet); Az strspn annak a string elejn lev, maximlis alkarakterlncnak a mrett szolgltatja, mely teljes egszben csak az strCharSetbeli karakterekbl ll. Ha a string nem strCharSetbeli karakterrel kezddik, akkor

C programnyelv

183

zrust kapunk. Nincs hibt jelz visszatrsi rtk. A keressbe nem rtendk bele a lnczr zrus karakterek. A fggvny visszaadja tulajdonkppen az els olyan karakter indext a stringben, mely nincs benn az strCharSet karakterlnccal definilt karakterkszletben. Az strcspn viszont annak a string elejn lev, maximlis alkarakterlncnak a mrett szolgltatja, mely egyetlen karaktert sem tartalmaz az strCharSetben megadott karakterekbl. Ha a string strCharSetbeli karakterrel kezddik, akkor zrust kapunk. Pldul a:
#include <string.h> #include <stdio.h> void main(void){ char string[] = "xyzabc"; printf("%s lncban az els a, b vagy c indexe %d\n", string, strcspn(string, "abc")); }

eredmnye:
xyzabc lncban az els a, b vagy c indexe 3

char *strstr(const char *string1, const char *string2); A fggvny string2 karakterlnc els elfordulsnak cmt szolgltatjk string1ben, ill. NULL mutatt kapunk, ha string2 nincs meg string1ben. Ha string2 res karakterlnc, akkor a rutin string1gyel tr vissza. A keressbe nem rtendk bele a lnczr zrus karakterek. char *strtok(char *strToken, const char *strDelimit); A fggvny a kvetkezleg megtallt, strTokenbeli szimblum cmvel tr vissza, ill. NULL mutatval, ha nincs mr tovbbi szimblum az strToken karakterlncban. Mindenegyes hvs mdostja az strToken karakterlncot, gy hogy \0 karaktert tesz a bekvetkezett elvlasztjel helyre. Az strDelimit karakterlnc az strTokenbeli szimblumok lehetsges elvlaszt karaktereit tartalmazza. Az els strtok hvs tlpi a vezet elvlasztjeleket, visszatr az strTokenbeli els szimblum cmvel, s ezeltt a szimblumot \0 karakterrel zrja. Az strToken maradk rsze tovbbi szimblumokra bonthat jabb strtok hvsokkal. Mindenegyes strtok hvs mdostja az strToken karakterlncot, gy hogy \0 karaktert tesz az aktulisan visszaadott szimblum vgre. Az strToken kvetkez szimblumt az strToken paramter helyn NULL mutats strtok hvssal lehet elrni. A NULL mutat els paramter hatsra az strtok megkeresi a kvetkez szimblumot a mdostott strTokenben. A lehetsges elvlasztjeleket tartalma-

184

MUTATK

z strDelimit paramter, s gy maguk az elvlaszt karakterek is, vltozhatnak hvsrlhvsra. A pldaprogramban ciklusban hvjuk az strtok fggvnyt, hogy megjelentethessk az s karakterlnc sszes szkzzel, vesszvel, stb. elvlasztott szimblumt:
#include <string.h> #include <stdio.h> void main( void ){ char s[] = "Szimblumok\tkarakterlnca\n ,, s nhny tovbbi szimblum"; char selv[] = " ,\t\n", *token; printf("%s\nSzimblumok:\n", s); /* Mg vannak szimblumok a karakterlncban, */ token = strtok(string, selv); while(token != NULL){ /* addig megjelentetjk ket, s */ printf("\t%s\n", token); /* vesszk a kvetkezket: */ token=strtok(NULL, selv); } }

A kimenet a kvetkez:
Szimblumok karakterlnca ,, s nhny tovbbi szimblum Szimblumok: Szimblumok karakterlnca s nhny tovbbi szimblum

Megoldand feladatok: Ksztse el az albb felsorolt, ismert C fggvnyek mutatkat hasznl vltozatt! A char * visszatrs rutinok az eredmny cmvel trnek vissza. int getline(char *, int): sor beolvassa a szabvny bemenetrl. char *squeeze(char *s, int c): c karakter trlse az s karakterlncbl a sajt helyn. int *binker(int x, int *t, int n): a nvekvleg rendezett, n elem, t tmbben megkeresend x csak mutatkat hasznl, binris keressi algoritmussal! Ha megvan, vissza kell adni a tbeli elforduls cmt. Ha nincs, NULL mutatt kell szolgltatni. int atoi(char *): karakterlnc egssz konvertlsa.

C programnyelv

185

char *strupr(char *): karakterlnc nagybetss alaktsa a sajt helyn. char *strrev(char *): karakterlnc megfordtsa a sajt helyn. char *strset(char *s, int c): s karakterlnc feltltse c karakterrel. char *strstr(const char *, const char *): a msodik karakterlnc keresse az elsben. A pontos ismertets kicsit elbbre megtallhat! Ksztsen char *strstrnext(const char *, const char *) fggvnyt is, mely ugyanazt teszi, mint az strstr, de static mutat segtsgvel az els hvs utn a msodik karakterlnc kvetkez elsbeli elfordulsnak cmvel tr vissza, s ezt mindaddig teszi, mg NULL mutatt nem kell szolgltatnia. 9.5.2 Vltoz paramterlista Az OBJEKTUMOK S FGGVNYEK szakaszbl tudjuk, hogy a fggvny paramterlistja tal is vgzdhet, amikor is a fordt a , eltti fix paramtereket a szoksos mdon kezeli, de a ,.. helyn ll aktulis paramtereket gy manipullja, mintha nem adtak volna meg fggvny prototpust. A szabvnyos STDARG.H fejfjlban definilt tpus s fggvnyek (makrk) segtsget nyjtanak az ismeretlen paramterszm s tpus paramterlista feldolgozsban. A tpus s az ANSI kompatibilis makrdefincik a kvetkezk lehetnek pldul:
typedef char *va_list; #define _INTSIZEOF(x) ((sizeof(x)+sizeof(int)-1)&\ ~(sizeof(int) -1)) #define va_start(ap, utsofix) (ap=(va_list)&utsofix+\ _INTSIZEOF(utsofix)) #define va_arg(ap, tipus) (*(tipus *)((ap+=_INTSIZEOF (tipus)) - _INTSIZEOF(tipus))) #define va_end(ap) ap = (va_list)0

A va_... szerkezettel csak olyan vltoz paramteres fggvnyek rhatk, ahol a vltoz paramterek a paramterlista vgn helyezkednek el. A va_... makrkat a kvetkezkpp kell hasznlni: 1. A szerkezetet hasznl fggvnyben deklarlni kell egy va_list tpus, mondjuk, param nev vltozt:
va_list param;

186

MUTATK , ami a va_arg s a va_end ltal ignyelt informcit hordoz mutat.

2. Meg kell hvni a va_start fggvnyt:


va_start(param, utsofix);

a va_arg s a va_end fggvnyek hasznlata eltt. A va_start a param mutatt a vltoz paramterlistval meghvott fggvny els vltoz paramterre lltja. Az utsofix paramterben el kell rni a vltoz paramterlistval meghvott fggvny azon utols formlis paramternek nevt, amely mg rgztett. A va_startnak nincs visszaadott rtke. A va_start els paramternek va_list tpusnak kell lennie. Ha a msodik paramter register trolsi osztly, akkor a makr viselkedse nem meghatrozott. 3. Eztn va_arg hvsok kvetkeznek:
(tipus) va_arg(param, tipus);

, melyek rendre szolgltatjk a vltoz paramterlista kvetkez aktulis paramternek rtkt. A makrbl ltszik, hogy a tipus megadsa fontos, hisz eszerint lpteti a param a mutatt elre, ill. ilyen tpus rtket szolgltat a vltoz paramterlistban soron kvetkez paramterbl. Azt aztn, hogy a vltoz paramterlistnak mikor van vge, a programoznak kell tudnia! 4. Ha a va_arg kiolvasta a vltoz paramterlista minden elemt, vagy ms miatt kell abbahagynunk a tovbbi feldolgozst, akkor meg kell hvni a
va_end(param);

makrt, hogy a vltoz paramterlistval meghvott fggvnybl biztostsuk a normlis visszatrst. A va_endnek nincs visszaadott rtke, s a makrdefincibl ltszik, hogy NULLzza a param mutatt. Tekintsnk meg a kvetkez egyszer pldt, melyben zrus jelzi a paramterlista vgt!
#include <stdio.h> #include <stdarg.h> void sum(char *uzen, ...) { int osszeg = 0, tag; va_list param; va_start(param, uzen); while(tag = va_arg(param, int)) osszeg += tag;

C programnyelv
printf(uzen, osszeg); } void main(void) { sum("1+2+3+4 = %d\n", 1, 2, 3, 4, 0); }

187

A param char * tpus. Ttelezzk fel a 32 bites int s cm esett! Akkor a va_start(param, uzen) hatsra az
_INTSIZEOF(uzen)= (sizeof(uzen)+sizeof(int)-1)&~(sizeof(int) -1)= (4+4-1)&~(4-1)=7&~3=4

s gy a
param=(char *)&uzen+4

, ami azt jelenti ugye, hogy param az els vltoz paramter cmt fogja tartalmazni. A tag=va_arg(param, int) kvetkeztben
tag=(*(int *)((param+=_INTSIZEOF(int))-_INTSIZEOF(int)))

, azaz:
tag=*(int *)((param+=4)-4)

Teht a tag vltoz felveszi a kvetkez, int, aktulis paramter rtkt, s ekzben param mr a kvetkez aktulis paramterre mutat. A kvetkez fggvnyek a parlist vltoz paramterlisttl eltekintve ugyangy dolgoznak, mint nevkben v nlkli prjaik. A felsorolt printf fggvnyek a 3. pont va_arg hvsait maguk intzik, s a format karakterlnc alapjn a vltoz paramterlista vgt is ltjk. A parlist az aktulis vltoz paramterlista els elemre mutat mutat. int vfprintf(FILE *stream, const char *format, va_list parlist); int vprintf(const char *format, va_list parlist); int vsprintf(char * puffer, const char * format, va_list parlist); 9.6 Mutattmbk Mutattmb a
tpus *azonost[<konstans-kifejezs>] <={inicializtorlista}>;

deklarcival hozhat ltre. Az azonost a mutattmb neve: konstans mutat, melynek tpusa (tpus **), vagyis tpus tpus objektumra mutat mutatra mutat mutat. Teht olyan konstans mutat, mely (tpus *) mutatra mutat. Vegyk szre a * mutatkpz opertor rekurzv hasznlatt!

188

MUTATK

A tmb egy eleme viszont (tpus *) tpus vltoz mutat. Kezdjnk el egy pldt magyar krtyval! A szneket kdoljuk a kvetkezkpp: makk (0), piros (1), tk (2) s zld (3). A krtykat egy sznen bell jelljk gy: hetes (0), nyolcas (1), kilences (2), tzes (3), als (4), fels (5), kirly (6) s sz (7). A konkrt krtya kdja gy keletkezik, hogy a sznkd utn lerjuk a krtya kdjt. Ilyen alapon a 17 pldul piros sz. Ksztsnk char *KartyaNev(int kod) fggvnyt, mely visszaadja a paramter kod krtya megnevezst!
/* KARTYA.H fejfjl. */ /* Szimbolikus llandk: */ #define SZINDB 4 /* Sznek szma. */ #define KTYDB 8 /* Krtyk szma egy sznen bell. */ #define OSZTO 10 /* Szn s krtyakd sztvlasztshoz*/ #define PAKLI SZINDB*KTYDB /* Krtyaszm a pakliban. */ #define MAXKOD (SZINDB-1)*OSZTO+KTYDB /* Max. krtykd.*/ /* Prototpusok: */ char * KartyaNev(int); void UjPakli(void); int Mennyi(void); int UjLap(void); /* KARTYA.C: Adat s fggvnydefincik. */ #include <string.h> #include <stdlib.h> #include <time.h> #include "KARTYA.H" static char *szin[SZINDB]={"makk ", "piros ", "tk ", "zld "}; static char *kartya[KTYDB]={"VII", "VIII", "IX", "X", "als", "fels", "kirly", "sz"}; char * KartyaNev(int kod){ static char szoveg[20]; if(kod>=0 && kod/OSZTO<SZINDB && kod%OSZTO<KTYDB){ strcpy(szoveg, szin[kod/OSZTO]); strcat(szoveg, kartya[kod%OSZTO]); } else strcpy(szoveg, "zldsg"); return szoveg; }

Vigyzzunk nagyon a tpusokkal! A szin s a kartya statikus lettartam, bels kapcsolds, fjl hatskr, karakterlncokra mutatk tmbjeinek kezdeteire mutat mutat (char **) konstansok nevei. A szin[1] a piros karakterlnc kezdetnek cmt tartalmaz, karakteres mutattmb elem (char *). Igazak pldul a kvetkezk:

C programnyelv
*szin szin[0] **szin *szin[0] m

189

A szoveg statikus lettartam karaktertmb, gy nem sznik meg a memriafoglalsa a fggvnybl val visszatrskor, csak loklis hatskr azonostjval nem rhet el! rjunk rvid, kiprbl programot!
/* PELDA23.C: Krtya megnevezsek kiratsa. */ #include <stdio.h> #include "KARTYA.H" void main(void){ int i; printf("Magyar krtya megnevezsek rendre:\n"); for(i=-1; i<=MAXKOD; ++i) printf("%-40s", KartyaNev(i)); }

9.7 Tbbdimenzis tmbk A tbbdimenzis tmbket a tmb tpus tmbjeiknt konstrulja meg a fordt. A deklarci
tpus azonost[<mret1>][mret2]. . .[mretN] <={inicializtorlista}>;

alak. Az elhelyezsrl egyelre annyit, hogy sorfolytonos, azaz a jobbra ll index vltozik gyorsabban. Pldul a
double matrix[2][3];

sorfolytonosan a kvetkez sorrendben helyezkedik el a trban:


matrix[0][0], matrix[0][1], matrix[0][2], matrix[1][0], matrix[1][1], matrix[1][2]

Egy elem elrse pldul


azonost[index1][index2]. . .[indexN]

mdon megy, ahol az indexekre igazak a kvetkezk:


0 <= index1 < mret1 0 <= index2 < mret2 ... 0 <= indexN < mretN

Vigyzzunk az indexek klnkln []be rsra is! Vve mondjuk a matrix[i][j]t, a matrix[i,j] kifejezs is rtelmes a Cben. Persze nem matrix[i][j]t jelenti, hanem a vessz opertor vgrehajtsa utn a matrix[j] tmbt. A BEVEZETS S ALAPISMERETEK szakasz Inicializls fejezetben a tmbk inicializlsrl mondottak most is rvnyben vannak, de

190

MUTATK

a tbbdimenzis tmb tovbbi tmbkbl ll, s gy az inicializlsi szablyok is rekurzvan alkalmazandk. Az inicializtorlista egymsba gyazott, egymstl vesszvel elvlasztott inicializtorok s inicializtorlistk sorozata a sorfolytonos elhelyezsi rend betartsval. Pldul a 4x3as t tmb els sornak minden eleme 1 rtk, msodik sornak minden eleme 2 rtk, s gy tovbb.
int t[4][3]={{1, 1, 1}, {2, 2, 2}, {3, 3, 3}, {4, 4, 4}};

Ha az inicializtorlistban nincs begyazott inicializtorlista, akkor az ott felsorolt rtkek az alaggregtumok, s azon bell az elemek sorrendjben veszik fel az aggregtum elemei. Az elz pldval azonos eredmnyre vezetne a kvetkez inicializls is:
int t[4][3] = {1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4};

Kapcsos zrjelek akr az egyes inicializtorok kr is tehetk, de ha a fordtt nem kvnjuk becsapni, akkor clszer ket az aggregtum szerkezett pontosan kvetve hasznlni! Az
int t[4][3]={{1, 1, 1}, {2, 2, 2}, {3, 3, 3}};

hatsra a t mtrix utols sorra (a t[3] tmbre) nem jut inicializtor, gy a t[3][0], t[3][1] s t[3][2] elemek mind zrus kezdrtket kapnak. Az
int t[4][3]={{1}, {2}, {3}};

eredmnyeknt a t[0][0] 1, a t[1][0] 2, a t[2][0] 3 lesz, s a tmb sszes tbbi eleme zrus. Tudjuk, hogyha nem adjuk meg, akkor az inicializtorlista elemszmbl llaptja meg a tmbmretet a fordt. Tbbdimenzis tmb deklarcijban ez azonban csak az els mretre vonatkozik, a tovbbi mreteket mind ktelez elrni.
int a[][]={{1, 1}, {2, 2}, {3, 3}}; int t[][3]={{1, 1, 1}, {2, 2, 2}, {3, 3, 3}}; /* HIBS */ /* OK */

Folytassuk tovbb a PELDA23.Cben megkezdett magyar krtys pldnkat! jabb programunknak legyen az a feladata, hogy megkeveri a paklit, s kioszt t lapot! Aztn jra kioszt t lapot, s gy tovbb mindaddig, mg a krtya el nem fogy. jabb kevers kvetkezik s jabb osztsok. A szoftvernek akkor van vge, ha mr nem krnek tbb lapot. Azt, hogy milyen krtykat osztott mr ki a paklibl, statikus lettartam, csak a KARTYA.C forrsfjlban elrhet, ktdimenzis, SZINDB* KTYDBs, kty tmbben tartja nyilvn a program. A megfelel tmbelem zrus, ha mg pakliban van a lap, s 1gy vlik, ha kiosztjk. A statikus,

C programnyelv

191

a KARTYA.C forrsmodulra ugyancsak loklis, ktydb vltozban a pakli aktulis krtyaszmt rzi a szoftver. A kvetkez sorokkal mindig a KARTYA.C bvtend!
static int kty[SZINDB][KTYDB]; static int ktydb=SZINDB*KTYDB;

Az UjPakli fggvny ugyanezt az llapotot lltja el.


void UjPakli(void){ int i, j; for(i=0; i<SZINDB; ++i) for(j=0; j<KTYDB; ++j) kty[i][j]=0; ktydb=SZINDB*KTYDB; }

A Mennyi rutinnal lekrdezhet, hogy mg hny krtya van a pakliban.


int Mennyi(void){ return ktydb; }

Az int UjLap(void) fggvny egy lapot ad a paklibl, azonban ezt vletlenszeren teszi a kvetkez technikval: Ha nincs krtya a pakliban, 1et szolgltat. Ha egyetlen lapbl ll a pakli, akkor azt adja. Ha ktydb<KTYDB, akkor elllt egy 1 s ktydb kzti vletlenszmot. Vgigjrja a paklit, s visszaadja a vletlenszmadik, mg nem kiosztott krtyt. Ha ktydb>=KTYDB, akkor vletlen sznt s vletlen krtyt vlaszt. Kiadja a lapot, ha mg eddig nem osztotta ki. Ha a krtya mr nincs a pakliban, jat vlaszt, s gy tovbb. Ltszik, hogy szksgnk lesz vletlenszm genertorra! 9.7.1 Vletlenszm genertor Hasznlathoz az STDLIB.H fejfjlt kell bekapcsolni. Egsz szmok pszeudvletlen sorozatt generlja 0 s RAND_MAX kztt az int rand(void); fggvny. A rutinnak nincs hibs visszatrse. A vletlenszm generls kezdrtkt lehet belltani a void srand(unsigned int kezd); fggvnnyel. Az alaprtelmezett indul rtk 1, gy ilyen rtk kezd paramterrel mindig jrainicializltathatjuk a vletlenszm genertort, azaz

192

MUTATK

a rand hvsok ugyanazt a vletlenszm sorozatot produkljk srand(1) utn, mintha mindenfle srand megidzs nlkl hasznltuk volna a randot. Az srandot a vletlenszer indulst is biztostand rendszerint a TIME.Hban helyet foglal time_t time(time_t *timer); fggvny kezd paramterrel szoktk meghvni. A time rutin az aktulis rendszer idt 1970. janur elseje jfl ta eltelt msodpercek szmban, time_t (long) tpusban szolgltatja. Nincs hibs visszatrs. A visszatrsi rtket a timer cmen is elhelyezi, ha a paramter nem NULL mutat. NULL mutat aktulis paramter esetn viszont nem tesz ilyet. Az srand fggvny szoksos hvsa teht:
srand(time(NULL))

Vletlenszm genertorral kockadobs rtket a kvetkezkpp produklhatunk:


rand()%6 + 1

Ha a fejleszt rendszerben nincs lebegpontos vletlenszmot generl fggvny, akkor 0 s 1 kztti vletlenszmokat a kvetkez kifejezssel llthatunk el:
(double)rand()/RAND_MAX

Folytassuk az UjLap fggvnyt!


int UjLap(void){ int i, j, db; if(ktydb==SZINDB*KTYDB) srand(time(NULL)); if(ktydb){ if(ktydb>=KTYDB) while(kty[i=rand()%SZINDB][j=rand()%KTYDB]); else{ db=ktydb>1?rand()%ktydb+1:1; for(i=0; i<SZINDB; ++i){ for(j=0; db&&j<KTYDB; ++j) if(!kty[i][j]&&!(--db)) break; if(!db) break; } } --ktydb; kty[i][j]=1; return i*OSZTO+j; } else return (-1); }

Elkszltnk a kibvtett KARTYA.Cvel, s most rjuk meg a mkdtet PELDA24.C programot!

C programnyelv
/* PELDA24.C: t lap osztsa. */ #include <stdio.h> #include <ctype.h> #include "KARTYA.H" #define LAPDB 5 void main(void){ int i, c; printf("Zsugzs: %d lap leosztsa:\n", LAPDB); while(printf("Ossza mr (I/N)? "), (c=toupper(getchar()))!='N'){ if(c=='I'){ putchar('\n'); if(Mennyi()<LAPDB) UjPakli(); for(i=0; i<LAPDB; ++i) printf("%-15s", KartyaNev(UjLap())); printf("\n\n"); } while(c!=EOF&&c!='\n') c=getchar(); } }

193

Megoldand feladatok: Javtson a kzlt programon a kvetkez mdon: A leosztott t lap megjelentetse trtnjk sznek szerint, s azon bell krtyk szerint rendezetten! A kapott t lapbl legyen legfeljebb 3 cserlhet! A jtk mkdjk francia krtyval! Ahhoz, hogy a tbbdimenzis tmbk bels szerkezett megrtsk, hozzunk ltre dinamikusan egy mtrixot! 9.7.2 Dinamikus memriakezels A C a memrit hrom rszre osztja. Az elsdleges adatterleten helyezi el a fordt a konstansokat s a statikus objektumokat. A loklis objektumokat s a fggvnyparamtereket a verembe (stack) teszi. A harmadik memriaterletet nevezzk heapnek, br ms nvvel is szoktk illetni futs kzben ri el a C program, s vltoz mret memria blokkok dinamikus alloklsra val. Pldul fk, listk, tmbk vagy brmi ms helyezhet el rajta. Az ismertetett, ANSI szabvnyos fggvnyek prototpusai az STDLIB.H fejfjlban tallhatk. void *calloc(size_t tetelek, size_t meret); A calloc tetelek*meret mret memria blokkot foglal, feltlti 0X00-val s visszaadja kezdcmt. Tulajdonkppen tetelek elemszm tmbnek foglal helyet, ahol egy elem mrete meret bjt.

194

MUTATK

Ha nincs elg hely, vagy a tetelek*meret szorzat rtke zrus, NULL mutatt kapunk vissza. void *malloc(size_t meret); A malloc legalbb meret bjtos memria blokkot foglal, nem tlti fel semmivel s visszaadja kezdcmt. A blokk nagyobb lehet meret bjtnl a trillesztshez ignyelt, plusz terlet s a karbantartsi informci elhelyezse miatt. Ha nincs elg hely a heap-en, NULL mutatt kapunk vissza a fggvnytl. Ha a meret 0, a malloc zrusmret blokkot allokl, s rvnyes mutatt ad vissza erre a terletre. J nhny szabvnyos fggvny is hvja a mallocot. Pldul a calloc, a getchar stb. A callockal, a mallockal lefoglalt, vagy a rgtn ismertetend reallockal jrafoglalt terlet trillesztse olyan, hogy brmilyen tpus objektum elhelyezsre alkalmas. A fggvnyektl visszakapott cm explicit tpusmdost szerkezettel brmilyen tpusv talakthat. Tegyk fel, hogy a program futsa kzben derl ki egy double tmb mrete! Ezt az rtket az int tpus, N vltoz tartalmazza. Hogyan lehet ltrehozni, kezelni, s vgl felszabadtani egy ilyen tmbt?
/* . . . */ int N; double *dtomb; /* . . . */ /* Itt kiderl, hogy mennyi N. */ /* . . . */ /* Ettl kezdve szksg van a tmbre. */ if((dtomb=(double *)malloc(N*sizeof(double)))!=NULL){ /* Sikeres a memriafoglals, azaz hasznlhat a tmb. Pldul a 6. eleme dtomb[5] mdon is elrhet. */ /* . . . */ /* Nincs szksg a tovbbiakban a tmbre. */ free(dtomb); /* . . . */ } else /* Hibakezels. */ /* . . . */

void *realloc(void *blokk, size_t meret); A realloc meret mretre szkti vagy bvti a korbban malloc, calloc vagy realloc hvssal alloklt memria blokkot, melynek kezdcmt megkapja a blokk paramterben. Sikeres esetben visszaadja az tmretezett memria blokk kezdcmt. Ez a cm nem felttlenl egyezik meg a

C programnyelv

195

blokk paramterben tadottal. Cmeltrs esetn a fggvny a korbbi memria blokk tartalmt tmozgatja az jba. Az esetleges rvidlstl eltekintve elmondhat, hogy az j blokk megrzi a rgi tartalmt. Ha az jraallokls sikertelen memria hiny miatt, ugyancsak NULL mutatt kapunk, de az eredeti blokk vltozatlan marad. void free(void *blokk); A free dealloklja vagy felszabadtja a korbban malloc, calloc vagy realloc hvssal alloklt memriaterletet, melynek kezdcmt megkapja a blokk paramterben. A felszabadtott bjtok szma egyezik az allokcikor (vagy a realloc esetn) ignyelttel. Ha a blokk NULL, a mutatt elhagyja a free, s rgtn visszatr. Az rvnytelen mutats (nem calloc, malloc, vagy realloc fggvnnyel foglalt memria terlet cmnek tadsa) felszabadtsi ksrlet befolysolhatja a rkvetkez allokcis krseket, s fatlis hibt is okozhat. Folytassuk a mtrixos feladatot!
/* PELDA25.C: Ktdimenzis tmb ltrehozsa dinamikusan.*/ #include <stdio.h> #include <stdlib.h> typedef long double TIPUS; typedef TIPUS **OBJEKTUM; int m=3, n=5; /* Sorok s oszlopok szma. */ int main(void) { OBJEKTUM matrix; int i, j; /* A sorok ltrehozsa: */ printf("%d*%d-s, ktdimenzis tmb ltrehozsa dinamikusan.\n", m, n); if(!(matrix=(OBJEKTUM)calloc(m, sizeof(TIPUS *)))){ printf("Ltrehozhatatlanok a mtrix sorai!\n"); return 1; } /* Az oszlopok ltrehozsa: */ for(i = 0; i < m; ++i) if(!(matrix[i]=(TIPUS *)malloc(n*sizeof(TIPUS)))){ printf("Ltrehozhatatlan a mtrix %d. sora!\n", i); while(--i>=0) free(matrix[i]); free(matrix); return 1; } /* Mestersges inicializls: */ for(i = 0; i < m; ++i) for(j = 0; j < n; ++j) matrix[i][j] = rand(); /* Kirs: */

196
printf("A mtrix tartalma:\n"); for(i = 0; i < m; ++i) { for(j = 0; j < n; ++j) printf("%10.0Lf", matrix[i][j]); printf("\n"); } /* Az oszlopok felszabadtsa: */ for(i = 0; i < m; ++i) free(matrix[i]); /* Sorok felszabadtsa: */ free(matrix); return 0; }

MUTATK

Vegyk szre, hogy a mainnek van visszaadott rtke! Zrust szolgltat, ha minden rendben megy, s 1et, ha memriafoglalsi problma lp fel. Figyeljk meg azt is, hogy a memria felszabadtsa foglalsval ppen ellenkez sorrendben trtnik, hogy a heapen ne maradjanak foglalt szigetek! A heap C konstrukci, s ha a program befejezdik, akkor maga is felszabadul, megsznik ltezni. sszestve: A matrix azonost a tmb kezdetre mutat vltoz mutat. A tmb kezdete viszont m vltoz mutatbl ll mutattmb. A mutattmb egy-egy eleme n elem, long double tpus tmb kezdetre mutat. A plda a rszek memriabeli elhelyezkedst is szemllteti, azaz: elbb az m elem mutattmbt allokljuk, aztn a mtrix els sora (0-s index) n elemnek foglalunk helyet. A mtrix msodik sora (1-s index) n elemnek helyfoglalsa kvetkezik. ... Legvgl a mtrix utols (m-1 index) sornak n elemt helyezzk el a memriban. A fordt ugyanezzel a mdszerrel dolgozik, de az ltala ltrehozott mtrixban a mutattmb konstans mutatkat tartalmaz, s a mtrix azonostja is konstans mutat. A tbbdimenzis tmbk tbbflekppen is szemllhetk. A fordt ltal ltrehozott
long double matrix[m][n];

mtrix pldjnl maradva:

C programnyelv

197

A matrix egy m elem vektor (tmb) azonostja. E vektor minden eleme egy n elem, long double tpus vektor. A matrix[i] (i = 0, 1, ..., m-1) az iedik, n long double elem vektor azonostja. A matrix[i][j] (i = 0, 1, ..., m-1 s j = 0, 1, ..., n-1) a mtrix egy long double tpus eleme. A dolgokat ms oldalrl tekintve! A matrix konstans mutat, mely az m elem, matrix[], konstans mutattmb kezdcmt tartalmazza (teht matrix[0]-t). A matrix+i e tmb i. elemnek cme. E tmb elemeinek tartalmt megszemllve lthatjuk, hogy n long double tpus vltoz mretvel trnek el egymstl. A matrix+i cm tartalma ugye *(matrix+i) vagy matrix[i]. A matrix[i] teht az i-edik, n darab long double elembl ll tmb azonostja: konstans mutat, mely az i-edik, n long double elem tmb kezdetre mutat. A matrix konstans mutat e konstans mutattmb kezdcmt tartalmazza. A matrix[i]+j vagy *(matrix+i)+j az iedik, n long double elem tmb j-edik elemnek cme. E cm tartalma elrhet a kvetkez hivatkozsokkal:
matrix[i][j], *(matrix[i]+j) vagy *(*(matrix+i)+j).

A &matrix[0][0], a matrix[0], a &matrix[0] s a matrix ugyanaz az rtk, azaz a mtrix kezdetnek cme, de a matrix[0][0] egy long double azonostja, a matrix[0] egy n long double elem tmb azonostja, s a matrix a tmbkbl ll tmb azonostja. gy:
&matrix[0][0] + 1 &matrix[0][1], matrix[0] + 1 *matrix + 1 &matrix[0][1], &matrix[0] + 1 matrix + 1 s matrix + 1 &matrix[1] &matrix[1][0].

32 bites cmeket felttelezve, 1000rl indulva, m=3 s n=5 esetn a matrix a kvetkezkppen helyezkedhet el a memriban:

198 matrix[0] matrix: 1012 1000 1012 1062 1112 matrix[1] 1062 1004 1022 1072 1122 matrix[2] 1112 1008 1032 1082 1132

MUTATK

matrix[0]:matrix[0][0] matrix[0][1] matrix[0][2] matrix[0][3] matrix[0][4] 1042 1092 1142 1052 1102 1152 matrix[1]:matrix[1][0] matrix[1][1] matrix[1][2] matrix[1][3] matrix[1][4] matrix[2]:matrix[2][0] matrix[2][1] matrix[2][2] matrix[2][3] matrix[2][4] A hromdimenzis tmbt gy valstja meg a fordt, hogy ltrehoz elbb egy mutattmbt, melynek mindenegyes eleme egy, az elzekben ismertetett szerkezet mtrixra mutat. Nhny sz mg a tbbszrsen alkalmazott index opertorrl! A kifejezs1[kifejezs2][kifejezs3]...ban az index opertorok balrl jobbra ktnek, gy a fordt elszr a legbaloldalibb kifejezs1[kifejezs2] kifejezst rtkeli ki. A szletett mutat rtkhez aztn hozzadva kifejezs3 rtkt j mutat kifejezst kpez, s ezt a folyamatot a legjobboldalibb index kifejezs sszegzsig vgzi. Ha a vgs mutat rtk nem tmb tpust cmez, akkor az indirekci mvelete kvetkezik. Teht pldul:
matrix[2][3] (*(matrix+2))[3] *(*(matrix+2)+3)

9.8 Tmbk, mint fggvnyparamterek Ha van egy


float vektor[100];

tmbnk, s kezdcmvel meghvjuk az


fv(vektor)

fggvnyt, akkor a fggvny defincijnak


void Fv(float *v) { /* . . . */ }

vagy
void Fv(float v[]){ /* . . . */ }

mdon kell kinznie. Az utbbi alakrl tudjuk, hogy a fordt rgtn s automatikusan tkonvertlja az elz (a mutats) formra.

C programnyelv

199

Ne feledjk, hogy ugyan a vektor konstans mutat a hv fggvnyben, de v (cmmsolat) mr vltoz mutat a meghvott fggvnyben. Vele teht elvgezhet pldul a v++ mvelet. A *v, a *(v+i) vagy a v[i] balrtk alkalmazsval a vektor tmb brmelyik eleme mdosthat a meghvott fggvnyben. Emlkezznk arra is, hogy a meghvott fggvnyt is tjkoztatni kell valahogyan a tmb mretrl. Pldul gy, hogy a mretet is tadjuk paramterknt. Az itt elmondottak tbbdimenzis tmbk vonatkozsban is igazak, de ott mr nem ismteljk meg! Ha van egy
float matrix[10][20];

defincink, s az azonostval meghvjuk


Fvm(matrix)

mdon az Fvm fggvnyt, akkor hogyan kell az Fvm defincijnak kinznie? A


void Fvm(float **m) { /* . . . */ }

prblkozs rossz, mert a formlis paramter float mutatra mutat mutat. A


void Fvm(float *m[20]) { /* . . . */ }

vltozat sem j, mert gy a formlis paramter 20 elem, float tpus objektumokra mutat mutattmb. Ennl a ksrletnl az az igazi problma, hogy a [] opertor prioritsa nagyobb, mint *-. Neknk formlis paramterknt 20 float elem tmbre mutat mutatt kne tadni. Teht a helyes megolds:
void Fvm(float (*m)[20]) { /* . . . */ }

vagy a tradicionlis mdszer szerint:


void Fvm(float m[][20]) { /* . . . */ }

, amibl rgtn s automatikusan ellltja a fordt az elz (a mutats) alakot. Meg kell mg emltennk, hogyha a tbbdimenzis tmbt dinamikusan hozzuk ltre, akkor az elzleg ajnlott megolds nyilvnvalan helytelen. A mtrix horgonypontjt ebben az esetben
float **matrix;

mdon definiljuk, ami ugye float mutatra mutat mutat. Teht ilyenkor a
Fvmd(matrix)

200

MUTATK

mdon hvott Fvmd fggvny helyes formlis paramtere:


void Fvmd(float **m) { /* . . . */ }

Megoldand feladatok: Ksztsen programot kt mtrix sszeadsra! A mtrixoknak ne dinamikusan foglaljon helyet a memriban! A mtrixok mrete azonban csak futs idben vlik konkrtt. rjon szoftvert kt mtrix sszeadsra s szorzsra! A mtrixok mrete itt is futs kzben dl el! A programban hasznljon fggvnyeket a mtrix mretnek s elemeinek bekrshez, valamint a kt mtrix sszeszorzshoz! A kt utbbi fggvny paramterknt kapja meg a mtrixokat! 9.9 Parancssori paramterek Minden C programban kell lennie egy a programot elindt fggvnynek, mely konzol bzis alkalmazsok esetben a main fggvny. Most s itt csak a main paramtereivel s visszatrsi rtkvel szeretnnk foglalkozni! A paramterekrl llthatjuk, hogy: elhagyhatak s nem ANSI szabvnyosak. A main legltalnosabb alakja:
int main(int argc, char *argv[]);

A paramterek azonosti bizonyos, C nyelvet tmogat krnyezetekben ettl el is trhetnek, de funkcijuk akkor is vltozatlan marad. Az argc a main-nek tadott parancssori paramterek szma, melyben az indtott vgrehajtand program azonostja is benne van, s rtke gy legalbb 1. Az argv a paramter karakterlncokra mutat mutattmb, ahol az egyes elemek rendre: argv[0]: A fut program (meghajtnvvel s) ttal elltott azonostjra mutat mutat. argv[1]: Az els parancssori paramter karakterlncra mutat mutat.

C programnyelv argv[2]: Az msodik paramter karakterlnc kezdcme. ...

201

argv[argc - 1]: Az utols parancssori paramter karakterlncra mutat mutat. argv[argc]: NULL mutat. Megjegyezzk, hogy az argc s az argv main paramterek elrhetk az
extern int _argc; extern char **_argv;

globlis vltozkon t is (STDLIB.H)! A main lehetsges alakjai a kvetkezk:


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

Vegynk egy pldt!


/* PELDA26.C: Parancssori paramterek. */ #include <stdio.h> #include <stdlib.h> int main(int argc,char *argv[]){ int i=0; printf("Parancssori paramterek:\n"); printf(Argc rtke %d.\n, argc); printf(Az tadott parancssori paramterek:\n); for(i=0; i<argc; ++i) printf("\targv[%d]: %s\n", i, *argv++); return 0; }

Ttelezzk fel, hogy a programot a kvetkez parancssorral indtottuk:


PELDA26 elso_par sodik par 3 4 stop!

Ekkor a megjelen kimenet a kvetkez lehet:


Parancssori paramterek: Argc rtke 6. Az tadott parancssori paramterek: argv[0]: C:\C\PELDA26.EXE argv[1]: elso_par argv[2]: sodik par argv[3]: 3 argv[4]: 4 argv[5]: stop!

202

MUTATK

Beszljnk kicsit a printf utols, *argv++ kifejezsrl! Az argvt a main paramterknt kapja, teht csak cmmsolat, vagyis a mainben akr el is ronthat. Az argv tpusa char **, s funkcionlisan a parancssori paramter karakterlncok kezdcmeit tartalmaz mutattmb kezdetnek cme. A rajta vgrehajtott indirekcival a tpus char * lesz, s pp a mutattmb els elemt (argv[0]) rjk el. Az uttag ++ opertor miatt ekzben az argv mr a msodik mutattmb elemre (argv[1]) mutat. Elrjk ezt is, s mellkhatsknt az argv megint elbbre mutat egy tmbelemmel. Teht a ciklusban rendre vgigjrjuk az sszes parancssori paramtert. Jusson esznkbe, hogy a main-nek tadott parancssor maximlis hosszt korltozhatja az opercis rendszer! A legtbb opercis rendszerben lteznek
vltoz=rtk

alak, n. krnyezeti vltozk, melyek definiljk a krnyezetet (informcit szolgltatnak) az opercis rendszer s a benne fut programok szmra. Pldul a PATH krnyezeti vltoz szokta tartalmazni az alaprtelmezett keressi utakat a vgrehajthat programokhoz, a parancsinterpreter helyt rja el a COMSPEC, s gy tovbb. Az opercis rendszer termszetesen lehetsget biztost ilyen krnyezeti vltozk trlsre, megadsra, s rtkk mdostsra. Cbl a krnyezeti vltozk ltalban az STDLIB.Hban deklarlt, nem ANSI szabvnyos extern char **_environ; globlis vltozval rhetk el. Ez karakterlncokra mutat mutattmb, s a mutatott karakterlncok a vltoz=rtk alak krnyezeti vltozkat rjk le. Az utols utni krnyezeti vltoz karakterlncra mutat tmbelem itt is NULL mutat, mint az argvnl. A globlis vltoz nevt azrt nem rt pontostani a programfejleszt rendszer segtsgbl! A msik lehetsg az STDLIB.Hban deklarlt, szabvnyos, nem kis nagybet rzkeny char *getenv(const char *valtozo); fggvny, mely visszaad az aktulis krnyezet alapjn a valtozo nev krnyezeti vltoz rtkre mutat mutatt. Ha nincs ilyen vltoz az aktulis krnyezeti tblban, NULL mutatt kapunk. A visszakapott nem NULL mutatval azonban nem clszer s nem biztonsgos dolog a kr-

C programnyelv

203

nyezeti vltoz rtkt mdostani. Ehhez a nem szabvnyos putenv rutin hasznlata ajnlott. A krnyezeti vltoz nevnek a vgre nem kell kitenni az = jelet, azaz pldul a PATH krnyezeti vltozt a getenv("PATH") hvssal krdezhetjk le! Megoldand feladatok: Ksztsen programot, mely a JANI, fordtsi idben vltoztathat azonostj, krnyezeti vltozrl megllaptja, hogy ltezike! Ha ltezik, akkor eldnti, hogy rtke kicsi, nagy, vagy ms. A feladat fokozhat egyrszt gy, hogy a vltoz lehetsges rtkei is legyenek fordtsi idben mdosthatk, msrszt gy, hogy ne rgztsk kettben a lehetsges rtkek darabszmt! rjon szoftvert, mely a krnyezeti vltoz azonostjt s lehetsges rtkeit parancssori paramterekknt kapja meg, s megllaptsai az elz pldban megfogalmazottakkal azonosak! Ha a programot paramter nlkl indtjk, akkor tjkoztasson hasznlatrl! Ha expliciten nem deklarljuk void-nak, akkor a main-nek int tpus sttuszkddal kell visszatrnie az t indt programhoz (process), rendszerint az opercis rendszerhez. Konvenci szerint a zrus visszaadott rtk (EXIT_SUCCESS) hibtlan futst, s a nem zrus sttuszkd (EXIT_FAILURE 1) valamilyen hibt jelez. Magt a main-bl val visszatrst (mondjuk 1es sttuszkddal) megoldhatjuk a kvetkez mdok egyikvel:
return 1; exit(1);

Foglalkozzunk kicsit a programbefejezssel is! 9.9.1 Programbefejezs A return 1 csak a mainben kiadva fejezi be a program futst. Az STDLIB.H bekapcsolsakor rendelkezsre ll, mindegyik opercis rendszerben hasznlhat void exit(int statusz); void abort(void); fggvnyek mind befejezik annak a programnak a futst, amiben meghvjk ket akrmilyen mly rutin szintrl is. A statusz paramter rtkt visszakapja a befejezettet indt (vrakoz szl) program, mint kilpsi llapotot (exit status). A statusz rtket tveszi persze az opercis rend-

204

MUTATK

szer is, ha volt a befejezett program indtja. Zrus (EXIT_SUCCESS) llapottal szoks jelezni a norml befejezst. A nem zrus llapot valamilyen hibt kzl (EXIT_FAILURE 1). Az exit fggvny a program befejezse eltt meghv minden regisztrlt (lsd atexit!) kilpsi fggvnyt, kirti a kimeneti puffereket, s lezrja a nyitott fjlokat. Az abort alaprtelmezs szerint befejezi az aktulis programot. Megjelenteti pldul az
Abnormal program termination

zenetet az stderren, s aztn SIGABRT (abnormlis programbefejezs) jelet generl. Ha nem rtak kezelt (signal) a SIGABRT szmra, akkor az alaprtelmezett tevkenysg szerint az abort 3as sttuszkddal visszaadja a vezrlst a szl programnak. Szval nem rti a puffereket, s nem hv meg semmilyen kilpsi fggvnyt (atexit) sem. Az stderr a szabvny hibakimenet. Az atexit s a signal fggvnyekrl rgtn sz lesz a kvetkez fejezetben! Megoldand feladatok: Ksztsen programot, mely neveket olvas a szabvny bemenetrl! Egy sorban egy nv rkezik, s az res sor a bemenet vgt jelzi. A nv nagybetvel kezddik, s a tbbi karaktere kisbet. A feltteleket ki nem elgt nv helyett azonnal msikat kell krni a problma kijelzse utn! A neveket rendezze nvsorba, s listzza ki ket lapokra bontva! A feladat a kvetkezkpp fokozhat: Ha a nvben az angol bcbelieken kvl az kezetes kis s nagybetk is megengedettek. Ha a neveket kzl listn elrehtra lehet lapozni. Ha egy nevet csak egyszer lehet megadni, azaz a msodik bevitelt elutastja hibaknt a szoftver. Ha a programot v parancssori paramterrel indtjk, akkor a rendezs visszafel halad a nvsoron. Ha a neveknek dinamikusan foglal helyet, kezdcmeiket mutattmbben helyezi el, s a rendezsnl a mutattmb elemeket cserlgeti, s nem a nv karakterlncokat a szoftver.

C programnyelv

205

9.10 Fggvny (kd) mutatk A mutatk fggvnyek n. belpsi pontjnak cmt is tartalmazhatjk, s ilyenkor fggvny vagy kdmutatkrl beszlnk. Ha van egy
int fv(double, int);

prototpus fggvnynk, akkor erre mutat mutatt


int (*pfv)(double, int);

mdon deklarlhatunk. A pfv azonost ezek utn olyan vltozt deklarl, melyben egy double, s egy int paramtert fogad s int-tel visszatr fggvnyek cmeit tarthatjuk. A pfv teht vltoz kdmutat. Kdmutat konstans is ltezik azonban, s ez a fggvnynv (a pldban az fv). Vigyzzunk a deklarciban a fggvnymutat krli kerek zrjel pr el nem hagyhatsgra, mert az
int *pfv(double, int);

olyan pfv azonostj fggvnyt deklarl, mely egy double, s egy int paramtert fogad, s int tpus objektumra mutat mutatval tr vissza. A problma az, hogy a mutatkpz opertor (*) prioritsa alacsonyabb a fggvnykpznl (()). Hogyan lehet rtket adni a fggvnymutatnak? Termszetesen a szoksos mdokon, azaz hozzrendelssel:
pfv = fv;

, ill. a deklarciban inicializtor alkalmazsval:


int fv(double, int); int (*pfv)(double, int) = fv;

Vigyzzunk nagyon a tpussal, mert az most bonyoldott! Csak olyan fggvny cme tehet be a pfv-be, mely a fggvnymutatval egyez tpus, azaz int-et ad vissza, egy double s egy int paramtert fogad ebben a sorrendben. A
void (*mfv)();

szerint az mfv meg nem hatrozott szm s tpus paramtert fogad olyan fggvnyre mutat mutat, melynek nincs visszatrsi rtke. Vegyk szre, hogy a krdses fggvnyek defincija eltt fggvnymutatk inicializlsra is hasznlhat a megadott fggvny prototpus!

206

MUTATK

Hogyan hvhatjuk meg azt a fggvnyt, melynek cmt a kdmutat tartalmazza? Alkalmaznunk kell a mutatkra vonatkoz klszablyunkat, ami azt mondja ki, hogy ahol llhat azonost a kifejezsben, ott llhat (*mutatazonost) is. Vegyk el jra az elz pldt! Ha
int a = fv(0.65, 8);

az fv fggvny hvsa, s valamilyen mdon lezajlott a pfv = fv hozzrendels is, akkor az


a = (*pfv)(0.65, 8);

ugyanaz a fggvnyhvs. Itt a pfv-re alkalmaztuk az indirekci opertort (*), de mivel ennek prioritsa alacsonyabb a fggvnyhvs opertornl (()), ezrt a *pfv-t kln zrjelbe kellett tenni! A kdmutatval kapcsolatos alapismeretek letrgyalsa utn felttlenl ismertetni kell a C fordt fggvnyekkel kapcsolatos fontos viselkedst: implicit konverzijt! Ha a kifejezs tpussal visszatr fggvny tpus, akkor hacsak nem cm opertor (&) mgtt ll, tpussal visszatr fggvnymutat tpusv konvertlja automatikusan s azonnal a fordt. Ez az implicit konverzi mindenek eltt megvalsul a
uttag-kifejezs(<kifejezslista>)

fggvnyhvsban, ahol az uttag-kifejezsnek kell tpussal visszatr fggvnycmm kirtkelhetnek lennie. A tpus a fggvnyhvs rtknek tpusa. A dolog praktikusan azt jelenti, hogy a fggvny brmilyen fggvnyre mutat kifejezssel meghvhat. Milyen mveletek vgezhetk a kdmutatkkal? Kpezhet a cmk. sizeof opertor operandusai lehetnek. Vgrehajthat rajtuk az indirekci mvelete is, mint lttuk. rtket kaphatnak, ahogyan azt az elzekben ismertettk. Meghvhatk velk fggvnyek. Ezt is ttekintettk. tadhatk paramterknt fggvnyeknek. Kdmutattmbk is ltrehozhatk.

C programnyelv Fggvny visszaadott rtke is lehet.

207

Explicit tpuskonverzival ms tpus fggvnymutatkk alakthatk. Kdmutatkra azonban nem alkalmazhat a mutataritmetika az egyenlsgi relci opertoroktl (== s !=) eltekintve. Foglalkozzunk a kdmutat paramterrel! A fggvnyekre rvnyes implicit tpuskonverzi a fggvnyparamterekre is vonatkozik. Ha a paramter tpussal visszatr fggvny, akkor a fordt automatikusan s rgtn tpus tpus rtket szolgltat fggvnyre mutat mutatv alaktja t. A kvetkez, kiss elvonatkoztatott pldban kitnen megszemllhet a kdmutat paramter fggvny prototpusban, ill. fggvny aktulis s formlis paramtereknt.
/* . . . */ long Emel(int); long Lep(int); long Letesz(int); void Munka(int n, long (* fv)(int)); /* . . . */ void main(void){ int valaszt=1, n; /* . . . */ switch(valaszt){ case 1: Munka(n, Emel); break; case 2: Munka(n, Lep); break; case 3: Munka(n, Letesz); } /* . . . */ } void Munka(int n, long (* fv)(int)){ int i; long j; for(i=j=0; i<n; ++i) j+=(*fv)(i); }

A kdmutat tpusa szerint az ilyen fggvny egy int paramtert fogad, s long rtket szolgltat. Kdmutatk paramterknt val tadst a Programbefejezs fejezetben mr megemltett, de ott nem trgyalt 9.10.1 atexit fggvny lersval is szemlltetjk! #include <STDLIB.H>

208 int atexit(void (cdecl * fv)(void));

MUTATK

Az atexit regisztrlja a paramter fggvnycmet, s norml programbefejezskor az exit meghvja az fv-t a szl programhoz val visszatrs eltt. Az fv fggvnymutat paramterbl ltszik, hogy a kilpsi fggvnyeknek nincs paramterk, s nem adnak vissza rtket. Az atexit mindenegyes hvsval ms-ms kilpsi fggvnyt regisztrltathatunk. A regisztrls veremszer, azaz a legutoljra regisztrlt fggvnyt hajtja vgre elszr a rendszer, s aztn gy tovbb visszafel. Az atexit a heapet hasznlja a fggvnyek regisztrlshoz, s gy a regisztrlhat kilpsi fggvnyek szmt csak a heap mrete korltozza. Az atexit sikeres hvskor zrust ad vissza, s nem zrust csak akkor kapunk tle, ha mr nem tud tbb fggvnyt feljegyezni. Pldul:
#include <stdio.h> #include <stdlib.h> void cdecl exitfv1(void){ printf("Exitfv1 vgrehajtva!\n"); } void cdecl exitfv2(void){ printf("Exitfv2 vgrehajtva!\n"); } int main(void){ atexit(exitfv1); atexit(exitfv2); printf("A main befejezdtt.\n"); return 0; }

A szabvny kimenet a kvetkez:


A main befejezdtt. Exitfv2 vgrehajtva! Exitfv1 vgrehajtva!

Folytassuk tovbb a kdmutatk trgyalst! Azt mondottuk, hogy kdmutatk tmbkben is elhelyezhetk. Visszatrve az els pfv-s pldnkhoz! Az
int (*pfvt[])(double, int) = {fv1, fv2, fv3, fv4, fv5};

deklarcival ltrehoztunk egy pfvt azonostj, olyan telem tmbt, mely int-et visszaad, egy double, s egy int paramtert fogad fggvnyek cmeit tartalmazhatja. Feltve, hogy fv1, fv2, fv3, fv4 s fv5 ilyen prototpus fggvnyek, a pfvt tmb elemeit kezdrtkkel is ellttuk ebben a deklarciban. Hvjuk mg meg, mondjuk, a tmb 3. elemt!

C programnyelv
a = (*pfvt[2])(0.65, 8);

209

Alaktsuk t fggvnymutat tmbt hasznlv a kdmutat paramternl ismertetett pldt! Az j megoldsunk mainen kvli rsze vltozatlan, a main viszont:
void main(void){ int valaszt=1, n; long (*fvmt[])(int) = {Emel, Lep, Letesz}; /* . . . */ Munka(n, fvmt[valaszt]); /* . . . */ }

A kdmutat visszatrsi rtkhez elemezzk ki a kvetkez fggvny prototpust! void (*signal(int jel, void (* kezelo)(int)))(int); A signal els paramtere int, a msodik (void (* kezelo)(int)) viszont rtket vissza nem ad, egy int paramtert fogad fggvnymutat tpus. Kitnen ltszik ezek utn, hogy a visszatrsi rtk void (*)(int), azaz rtket nem szolgltat, egy int paramtert fogad fggvnymutat. A visszaadott rtk tpusa teht a signal msodik paramternek tpusval egyezik. A SIGNAL.H fejfjlban elhelyezett prototpus signal fggvnnyel klnben a program vgrehajtsa sorn bekvetkez, vratlan esemnyeket (megszakts, kivtel, hiba stb.), n. jeleket lehet lekezeltetni. Tbbfle tpus jel ltezik. A void (*)(int) tpus kezelfggvnyt a manipullni kvnt jelre kln meg kell rni. A signal rutinnal hozzrendeltetjk a kezelt (2. paramter) az els paramter tpus jelhez, s a signal az eddigi kezel cmvel tr vissza. Egy bizonyos tpus fggvnymutat explicit tpuskonverzival
(tpusnv) eltag-kifejezs

talakthat ms tpus kdmutatv. Ha az e mdon tkonvertlt mutatval fggvnyt hvunk, akkor a hats a programfejleszt rendszertl, a hardvertl fgg. Viszont, ha visszakonvertljuk az talaktott mutatt az eredeti tpusra, akkor az eredmny azonos lesz a kiindulsi fggvnymutatval. Szedjk csak megint el az
int fv(double, int), a; int (*pfv)(double, int) = fv;

pldnkat, s legyen a

210
void (*vpfv)(double); vpfv=(void (*)(double))pfv;

MUTATK

A
(*vpfv)(0.65);

eredmnyessge elgg megkrdjelezhet, de a


pfv=(int (*)(double, int))vpfv;

utn teljesen rendben lesz a dolog:


a=(*pfv)(0.65, 8);

Emlkezznk csak! Explicit tpusmdostott kifejezs nem lehet balrtk. Foglalkozzunk csak jra egy kicsit a tpusnevekkel! 9.10.2 Tpusnv Explicit tpusmdostsban, fggvnydeklartorban a paramtertpus rgztsekor, sizeof operandusaknt stb. szksg lehet a tpus nevnek megadsra. Ehhez kell a tpusnv, mely szintaktikailag a krdses tpus objektum olyan deklarcija, melybl elhagytk az objektum azonostjt.
tpusnv: tpusspecifiktor-lista<absztrakt-deklartor> absztrakt-deklartor: mutat <mutat><direkt-absztrakt-deklartor> direkt-absztrakt-deklartor: (absztrakt-deklartor) <direkt-absztrakt-deklartor>[<konstans-kifejezs>] <direkt-absztrakt-deklartor>(<paramter-tpus-lista>)

Az absztrak-deklartorban mindig lokalizlhat az a hely, ahol az azonostnak kne lennie, ha a konstrukci deklarcin belli deklartor lenne. Lssunk nhny konkrt pldt!
int *

int tpus objektumra mutat mutat.


int **

int tpus objektumra mutat mutatra mutat mutat.

C programnyelv
int *[]

211

Nem meghatrozott elemszm, int tpus mutattmb.


int *()

Ismeretlen paramterlistj, intre mutat mutatval visszatr fggvny.


int (*[])(int)

int tpussal visszatr, egy int paramteres, meghatrozatlan elemszm fggvnymutat tmb.
int (*(*())[])(void)

Ismeretlen paramterezs, int tpussal visszatr fggvnymutatkbl kpzett, meghatrozatlan mret tmbre mutat mutatt szolgltat, paramterrel nem rendelkez fggvny. A problmn: a sok zrjelen, a nehezen rthetsgen typedef alkalmazsval lehet segteni. 9.11 Tpusdefinci (typedef) Az elemi tpusdefincirl sz volt mr a TPUSOK S KONSTANSOK szakasz vgn. Az ott elmondottakat nem ismteljk meg, viszont annyit jra szeretnnk tisztzni, hogy: A tpusdefinci nem vezet be j tpust, csak ms mdon megadott tpusok szinonimit lltja el. A typedef nv, ami egy azonost, szintaktikailag egyenrtk a tpust ler kulcsszavakkal, vagy tpusnvvel. A tpusdefinci bonyoltshoz elszr azt emltjk meg, hogy a
typedef tpus azonost;

szerkezetben az azonost a priorits sorrendjben lehet: azonost(): fggvny tpust kpz, uttag opertor. Pldul:
typedef double dfvdi(double, int); dfvdi hatvany;

, ahol a hatvany egy double, s egy int paramtert fogad s double-t visszaad fggvny azonostja. azonost[]: tmb tpust kpz, uttag opertor. Pldul:
typedef double dtomb[20]; dtomb t;

, ahol a t 20 double elembl ll tmb azonostja.

212

MUTATK

*azonost: mutat tpust kpz, eltag opertor. Pldul:


typedef short *shptr; shptr sptr;

, ahol az sptr short tpus objektumra mutat mutat azonostja. Ezek az opertorok egyszerre is elfordulhatnak az azonost-val. Pldul:
typedef int *itb[10]; itb tomb;

, ahol a tomb 10 elem int objektumokra mutat mutattmb azonostja. Megemltend mg, hogy az elzek alkalmazsval csnjn kell bnni, hisz az gy tpusdefinilt azonostknak ppen a jellege (fggvny, tmb, mutat) veszik el. A tpusdefinci tovbbi komplexitsa abbl fakad, hogy a
typedef tpus azonost;

szerkezetbeli tpus korbbi typedef tpus azonost;-ban definilt azonost is lehet. Teht a tpusdefincival ltrehozott tpuspecifiktor tpus lehet egy msik tpusdefinciban. Nzznk nhny pldt!
typedef int *iptr; typedef char nev[30]; typedef enum {no, ferfi, egyeb} sex; typedef long double *ldptr; ldptr ptr2; /* long double objektumra mutat mutat.*/ ldptr fv2(nev);/*30 elem karaktertmb paramtert fogad, long double objektumra mutat mutatval visszatr fggvny. */ typedef iptr (*ipfvi)(sex); ipfvi fvp1; /* int-re mutat mutatt visszaad, egy sex tpus enum paramtert fogad fggvnyre mutat mutat. */ typedef ipfvi ptomb[5]; ptomb tomb; /* 5 elem, int-re mutat mutatt szolgltat, egy sex tpus paramtert fogad fggvnyre mutat mutattmb. */ iptr fugg(ptomb);/*int-re mutat mutatt visszaad, 5 elem, int-re mutat mutatval visszatr, egy sex enum paramteres fggvnyre mutat mutattmbt paramterknt fogad fggvny. */

C programnyelv

213

A tpusdefincival a program tpusai parametrizlhatk, azaz a program portbilisabb lesz, hisz az egyetlen typedef mdostsval a tpus megvltoztathat. A komplex tpusokra megadott typedef nevek ezen kvl javtjk a program olvashatsgt is. Loklis szinten megadott tpusdefinci loklis hatskr is. Az ltalnosan hasznlt tpusdefincikat globlisan, a feladathoz tartoz fejfjlban szoktk elrni. Egy utols krds: Mikor ekvivalens kt tpus? Ha a kt tpusspecifiktor-lista egyezik, belertve azt is, hogy ugyanaz a tpusspecifiktor tbbflekppen is megadhat. Pldul: a long, a long int s a signed long int azonosak. Ha az absztrakt-deklartoraik a typedef tpusok kifejtse, s brmely fggvnyparamter azonost trlse utn ekvivalens tpusspecifiktor-listkat eredmnyeznek. A tpusekvivalencia meghatrozsnl a tmbmretek s a fggvnyparamter tpusok is lnyegesek. 9.12 Ellenrztt bemenet Jegyzetnkben minden feladat megoldsban azt sugalltuk, hogy: A programnak ellenriznie kell a bemenett. Ez a vizsglat termszetesen csak a konkrt adatok ismerete nlkl a lehetetlensgek, s a problmt okoz rtkk kiszrsre szortkozhat. Pldul: Ne etessnk kt tonns kutyt! Ne folystsunk nyugdjat 300 ves embernek! Nem tekinthet, csak legfeljebb mkedvel, programnak az, ami egy vletlenl elgpelt informci miatt feldobja a talpt! rjon int getint(int *) fggvnyt, mely ellenrztten beolvas egy egsz szmot a billentyzetrl gy, hogy a nem megengedett karaktereket nem is echzza a karakteres kpernyn (ablakban). A szm eltti fehr karakterek kzl az Entert soremelssel, s minden ms fehr karakter szkzzel echzand! Ezutn egy opcionlis eljelet kveten mr csak szmjegy karakterek kvetkezhetnek. Ha az els szmjegy zrus, akkor tovbbi szm karakterek sem jhetnek. A fggvny legyen portbilis, azaz mkdjn 16 s 32 bites intre egyarnt! Ez azt jelenti 16 bites esetre, hogy legfeljebb t szmjegyet echzhat, de legyen tekintettel az brzolsi korltokra is! Ha az els 4 szmjegy 3276nl nagyobb, akkor tbb szmjegyet mr nem fogadhat. Ha az els 4 szmjegy pontosan 3276, akkor tdik szmjegyknt nem fogadhatja a fggvny a 9et, s a 8at is

214

MUTATK

csak negatv egsz szm esetn. A szm megadst fehr karakterrel, Ctrl+Zvel vagy F6tal kell lezrni. A fehr karaktert a mr lert mdostott ech utn vissza kell adni a hvnak. Ctrl+Z vagy F6 esetn viszont EOF szolgltatand. A beolvasott egsz rtk konvertland s elhelyezend a paramter cmen! A cmen lev rtk azonban nem vltozhat meg, ha nem adtak meg egyetlen szmjegy karaktert sem. A rutin persze rvid programmal ki is prbland! A feladat megoldshoz szksgnk van kt konzolkezel fggvnyre. Az int putch(int c); rutin a c karaktert rja ki kzvetlenl (pufferezs nlkl) a konzol kpernyre (ablakba) az aktulis pozcitl, aktulis sznben s megjelensi attribtumokkal, s a kurzort eggyel elbbre lltja. Sikeres esetben a viszszakapott rtk maga a c karakter. A sikertelensget viszont EOF-fal jelzi a fggvny. Kivitelkor nincs transzlci, azaz a fggvny az LF ('\n') karakterbl nem llt el CR-LF ("\r\n") karakter prt. Megoldsunkban nem foglalkozunk majd a putch hibakezelsvel, mert felttelezzk, hogyha az opercis rendszer mkdik, akkor a konzol megy. int getch(void); Ech nlkl beolvas egyetlen karaktert a konzolrl (billentyzet), s ezt szolgltatja a hvnak. A bejv karakter rgtn rendelkezsre ll, s nincs pufferezs soremels karakterig. Funkci vagy nyl billenty letsekor a fggvnyt ktszer kell hvni, mert az els hvs zrussal tr vissza, s a msodik szolgltatja az aktulis gomb kdjt. A rutinnak nincs hibs viszszatrse. E fggvnyek nem szabvnyosak, de szinte minden opercis rendszerben rendelkezsre llnak kisebbnagyobb eltrsekkel a CONIO.H fejfjl bekapcsolsa utn.
/* PELDA27.C: Egszek beolvassa s visszarsa gy, hogy az rvnytelen karakterek echja meg sem trtnik.*/ #include <stdio.h> #include <conio.h> #include <limits.h> #if SHRT_MAX == INT_MAX #define HOSSZ 4 #else

C programnyelv
#define HOSSZ 9 #endif #define F6 64 #define CTRLZ 26 #define MAX INT_MAX/10 #define HATAR INT_MAX%10+'0'+1

215

A HOSSZ makr azt a szmjegy mennyisget rgzti 16, s 32 bites intre, ameddig mg nem kell foglalkozni a megadott szm karakter rtkvel. A MAX maga az a HOSSZ szmjegy rtk, amihez mg egy jegyet tve elrhet, de tl nem lphet a fels, vagy az als brzolsi korlt. 16 bites intnl ez az rtk 3276, amihez pozitv irnyban legfeljebb 7, s negatv irnyban maximum 8 jhet. A HATAR az a szmjegy karakter, ami mg negatv egsz esetben elfordulhat MAXot kveten megadhat karakterknt a HOSSZ+1. pozcin. 16 bites int szmra ez az rtk 8.
int getint(int *pn){/* Egsz beolvassa a bemenetrl. */ int c, /* A beolvasott karakter. */ sign=1, /* Eljel: pozitv +1, negatv -1. Alaprtelmezs a pozitv, a kezdrtk miatt. */ elojel=0, /* Volt-e mr eljel? */ hossz=0, /* A beolvasott szmjegy karakterek szma. */ null=0; /* A beolvasott szm zrus-e? */ while(!hossz) switch(c=getch()){ case ' ': case '\t': case '\r': if(!elojel) if(c!='\r')putch(' '); else {putch('\n'); putch('\r'); } break; case '+': case '-': if(!elojel){ putch(c); sign=(c=='+')?1:-1; ++elojel; } break; case '0': if(!elojel){ putch(c); *pn=0; ++hossz; null=1;} break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': putch(c); *pn=c-'0'; ++hossz; break; default: if(!c)c=getch(); if(c==CTRLZ||c==F6) return EOF; }

216

MUTATK

A getint lnyegben kt while ciklusra bonthat, melyek mindegyike egyegy switch. Az els addig tart, mg egy szmjegy karaktert meg nem adnak, vagy Ctrl+Zvel, ill. F6tal le nem zrjk a bemenetet. Az els switch: Vgrehajtja a fehr karakterekre elrt echt, de csak akkor, ha eljel karakter mg nem volt. Magyarn eljel utn nincs mr ech a fehr karakterekre. Az eljelet echzza a rutin, ha korbban mg nem rkezett, s rtkt megjegyzi a sign vltozban. Bejelli azt is, hogy volt mr eljel, hogy mg egyet ne tudjanak megadni. Az els szm karaktert echzza a fggvny, konvertlva kiteszi a paramter cmre, s a hossz vltozban szmllja is. Eljel utn nem enged mr meg zrust gpelni, ill. ha megadhat volt a nulla, akkor bejelzi bejvetelt a null vltozba. A default gon jabb olvass kveti az elz zrus berkezst. Az F6 msknt vizsglhat sem lenne.
while(1) switch(c=getch()){ case ' ': case '\t': case '\r':if(c!='\r')putch(' '); else {putch('\n'); putch('\r'); } *pn*=sign; return c; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': if(!null&&(hossz<HOSSZ|| (hossz==HOSSZ&&(*pn<MAX||*pn==MAX&& (sign==1&&c<HATAR||sign!=1&&c<=HATAR))))){ putch(c); *pn=*pn*10+c-'0'; ++hossz; } break; default: if(!c)c=getch(); if(c==CTRLZ||c==F6){ *pn*=sign; return EOF;} } } void main(void){ int i; printf("Egszek beolvassa CTRL+Z-ig.\n"); while(getint(&i)!=EOF) printf("%20d\n\r", i); }

C programnyelv A msodik while fehr karakterrel, vagy Ctrl+Zvel, ill. F6tal

217

zrul. A paramter cmen lev konvertlt rtket eljelhelyess teszi a rutin, majd visszatr a ki is echzott, fehr karakterrel, vagy az EOFfal. A szmjegy karakter echja, konverzija s szmllsa csak akkor trtnik meg, ha az els szm nem zrus volt s: HOSSZnl kevesebb karaktert adtak meg eddig, vagy pp annyit, de a paramter cmre konvertlt rtk kisebb MAXnl, ill. pont MAX s az utols szmjegy karakter megfelel az elrt, szigor feltteleknek. Megoldand feladat: rja gy t a getint fggvnyt, hogy a visszatrls (backspace) gombot funkcija szerinti mdon kezelni tudja! Remlhetleg mindenki el tudja kpzelni, hogy tovbb bonyoldna a dolog, ha tovbbi szerkeszt billentyk (Delete, Insert, balra nyl s jobbra nyl) hasznlatt is megengednnk, vagy Uram, bocs lebegpontos rtk bekrst vgeznnk s nem egszt. Magyarn: kitnen ltszik, hogy tbbe kerlne a leves, mint a hs. sszefoglalva: A bemenet ellenrzst sem szabad tlzsba vinni, de az klszablynl rott elveket a j programnak be kell tartania.

218

STRUKTRK S UNIK

10 STRUKTRK S UNIK
A struktra s az uni aggregtum. Egy vagy tbb, esetleg klnbz tpus vltoz (elnevezett tag) egyttese, melynek nll azonostja van. A struktrt ms nyelvben rekordnak nevezik. Azt teszi lehetv, hogy a valamilyen mdon sszetartoz vltozk egsz csoportjra egyetlen nvvel hivatkozhassunk, azaz hogy a vltozcsoport kezelse egyszerbb legyen. Tulajdonkppen minden struktrval s unival j, sszetett, felhasznli adattpust hozunk ltre. Az ANSI szabvny megengedi, hogy a struktrkat t lehessen msolni egymsba, hozz lehessen rendelni, s tadhatk legyenek fggvnyeknek, ill. rutinok visszatrsi rtke is lehessen struktra. Kpezhet termszetesen a struktra cme (&), mrete (sizeof), s benne lehet explicit tpusmdost szerkezetben is, de a struktrk nem hasonlthatk ssze. A struktrban felsorolt vltozkat struktratagoknak (member) nevezik. Struktratag kis megszortsokkal - melyre ksbb kitrnk - akrmilyen tpus lehet. Lehet alap s szrmaztatott tpus brmilyen sorrendben. A deklarcibeli tpusspecifiktor egyik alternatvja a
struktra-vagy-uni-specifiktor: struktra-vagy-uni<azonost>{struktratag-deklarcilista} struktra-vagy-uni azonost struktra-vagy-uni: struct union

A struktratag-deklarcilista struktra, ill. unitag deklarcik sorozata:


struktratag-deklarcilista: struktratag-deklarci struktratag-deklarcilista struktratag-deklarci struktratag-deklarci: tpusspecifiktor-lista struktra-deklartorlista tpusspecifiktor-lista: tpusspecifiktor tpusspecifiktor-lista tpusspecifiktor struktra-deklartorlista: struktra-deklartor struktra-deklartorlista, struktra-deklartor

C programnyelv

219

A struktra-deklartor tbbnyire a struktra, ill. az uni egy tagjnak deklartora. A struktratag azonban meghatrozott szm bitbl is llhat, azaz lehet n. bitmez (bit field) is, mely a nyelvben struktrkon kvl nem is hasznlhat msutt. A mez bitszlessgt a kettspontot kvet, egsz rtk konstans-kifejezs hatrozza meg.
struktra-deklartor: deklartor <deklartor>: konstans-kifejezs

A bitmezvel s az unival mg ebben a szakaszban foglalkozunk! 10.1 Struktradeklarci Alakja teht a kvetkez:
<trolsi-osztly-specifiktor> struct <struktracmke> <{ struktratag-deklarcilista }> <azonostlista>;

Pldul:
struct datum{ int ev, ho, nap, evnap; long datumssz; char datumlanc[11]; } d, dptr, dt[10]; /* Dtumsorszm. */ /* Azonostlista. */

, ahol: A trolsi-osztly-specifiktor elhagysval, megadsval s ennek rtelmezsvel nem foglalkozunk jra! A datum azonost ennek a struktrnak a cmkje (struktracmke), mely azt biztostja, hogy ksbb struct datum mdon hivatkozni tudjunk a felhasznli tpusra. Pldul: Az ev, a ho, a nap s az evnap a struct datum tpus struktra int tpus tagjainak, a datumssz a long tpus tagjnak s a datumlanc a struktra char* tpus tagjnak az azonosti. A tagneveknek csak a struktrn bell kell egyedieknek lennik, azaz a tagazonostk nyugodtan egyezhetnek pldul ms kznsges vltozk neveivel, vagy a struktracmkkkel. A d struct datum tpus vltoz, a dptr s az sptr struct datum tpus objektumra mutat mutatk, s a dt tz, struct datum tpus elembl ll tmb azonostja, azaz a dt struktratmb.
struct datum *sptr = (struct datum *)malloc(sizeof(struct datum));

220

STRUKTRK S UNIK

A fordt a struktrnak ppen annyi helyet foglal a memriban, hogy benne a struktra minden tagja elfrjen. A struktratagok a deklarci sorrendjben, folyamatosan nvekv cmeken helyezkednek el, s gy a struktradefinciban ksbb deklarlt tag cme mindig nagyobb. Az azonostlista nlkli struktradeklarcit, ahol van struktratag-deklarcilista, azaz megadott vlik a struktra szerkezete, szoks struktradefincinak is nevezni, ugyan nincs memriafoglalsa. Minden struktradeklarci egyedi struktra tpust hoz ltre, s gy
struct A int i, double struct B int i, double { j; d; } a, a1; { j; d; } b;

az a s az a1 objektumok struct A tpus struktrk, de az a s a b objektumok klnbz struktra tpusak annak ellenre is, hogy a kt struktra szerkezete azonos. Az azonostlista nlkli, de struktracmkvel s tag-deklarcilistval elltott struktradefinci nem foglal ugyan helyet a memriban, de biztostja azt a lehetsget, hogy a struktradeklarci hatskrben ksbb ilyen tpus struktrval azonostlistt is megadva helyet foglalhassunk vltozinknak, mutatinknak s tmbjeinknek. Pldul:
struct datum{ int ev, ho, nap, evnap; long datumssz; /* Dtumsorszm. */ char datumlanc[11]; }; /* . . . */ struct datum d, *dptr=&d, dt[10];

Fedezzk fel, hogy a felhasznl definilta tpusnv struct datum. Hasonltsul:


double d, *dptr=&d, dt[10];

Struktra, uni, enum deklarcit, defincit zr kapcsos zrjel utn ktelez pontosvesszt tenni, mert ez zrja az azonostlistt!
struct struki{ int a, b; float matrix[20][10]; char nev[26]; }; /* Itt nem elhagyhat a ; a } utn! */

C programnyelv

221

A struktracmknek egyedinek kell lennie a struktra, uni s enum cmke nvterleten! Mint a tmbknl, struktrknl is megadhat nem teljes tpusdeklarci. Pldul a
struct datum;

mg akkor is ltrehozza az aktulis hatskrben a struct datum nem teljes tpust, ha ilyen befoglal, vagy kls hatskrben is ltezne. Knnyen belthat, hogy ez struct datum tpus struktra objektum defincijra nem hasznlhat a struktra szerkezetnek kzbens, ugyanezen hatskrbeli definilsa nlkl, hisz ismeretlen a memriaigny. Arra azonban ez is alkalmas, hogy deklarciban, typedefben hasznljuk a tpusnevet, vagy hogy struct datum tpus objektumokra mutat mutatkat hozzunk ltre:
struct datum *dptr1, *dptr2, *dptrt[20];

Az ilyen mutatk akr ms struktra tagjai is lehetnek:


struct A; /* struct B{ /* struct A *pa;}; /* struct A{ struct B *pb;}; /* Nem teljes tpusdeklarci. */ Itt tag a nem tejes tpusra */ mutat mutat. */ Ez most mr teljes tpus lesz.*/

Vigyzat! Struktradefinciban a struktra tpusa a struktra-tagdeklarcilistban csak akkor vlik teljess, ha elrjk a specifiktor bezr kapcsos zrjelt (}). A struktradeklarci ltalnos alakjbl lthat volt, hogy belle a struktracmke is elhagyhat. Ha ezt megtesszk, n. nv nlkli, vagy cmkzetlen struktrhoz jutunk. Vilgos, hogy ebben az esetben a nem teljes tpusdeklarcinak
struct;

semmi rtelme (szintaktikai hiba is) sincs, de az olyan deklarcinak sincs, amiben csak a struktra szerkezett adjuk meg:
struct {int tag1, tag2; /* ... */};

hiszen ksbb nem tudunk a tpusra hivatkozni, s ebbl kvetkezleg ilyen tpus objektumokat deklarlni. Nv nlkli struktradeklarciban nem hagyhat el teht az azonostlista, azaz:
struct {int tag1, tag2; /* ... */} az1, az2[14];

222

STRUKTRK S UNIK

10.1.1 Tpusdefinci Az elbb vzolt problma tpusdefinci alkalmazsval thidalhat:


typedef struct{ int tag1, tag2; /* ... */} CIMKETLEN; /* Most sincs cmke. */ CIMKETLEN y, *y, ytomb[12];

A tpusdefinci a cmkzett struktrval is hasznlhat lett volna:


typedef struct datum{ int ev, ho, nap, evnap; long datumssz; /* Dtumsorszm. */ char datumlanc[11]; } DATUM; /* . . . */ DATUM d, *dptr, dt[10];

sszestve: A typedef cmke nlkli struktrk, unik s enum-ok tpusdefincijra is alkalmas. Struktrk esetben hasznljunk azonban struktracmkt, vagy typedef-es szerkezetet, de a kettt egytt nem javasoljuk! Egy kicsit sszetettebb pldt vve:
typedef char nev[30]; typedef enum{no, ferfi, egyeb} sex; typedef struct{ nev csalad, kereszt; /* Kt 30 elem karaktertmb.*/ sex fino; /* no vagy ferfi rtk enum.*/ /* . . . */ double osztondij; } hallgato; typedef hallgato evf[100];/* 100 elem, fenti szerkezet struktratmb. */ evf evf1, evf2, evf3; /* Hrom darab,100 elem, fenti szerkezet struktratmb. */

10.2 Struktratag deklarcik A { }-ben ll struktratag-deklarcilista a deklartor szintaktikt kvetve meghatrozza a struktratagok neveit s tpusait. A struktratag brmilyen tpus lehet a void, a nem teljes, vagy a fggvny tpustl eltekintve. A struktratag deklarci nem tartalmazhat azonban trolsi osztly specifiktort vagy inicializtort. Struktratag nem lehet az ppen definci alatt ll struktra sem:
struct szoszlo{ static char *szo; int szlo=0; /* HIBS */ /* HIBS */

C programnyelv
struct szoszlo elozo, kovetkezo; }; /* HIBS */

223

Struktratag lehet azonban nem teljes tpus struktrra, gy akr az ppen deklarci alatt llra mutat mutat:
struct szoszlo{ char *szo; int szlo; struct szoszlo *elozo, *kovetkezo; };

/* OK */

Struktratag lehet tmb, st mr definilt szerkezet struktra is:


struct sor_lanc{ int sorszam; char megjegyzes[32]; struct sor_lanc *kovetkezo; }; struct kereszthivatkozas{ char *szo; int szlo; struct kereszthivatkozas *elozo, *kovetkezo; struct sor_lanc elso; };

A struktrnak nem lehet fggvny tagja, de fggvnyre mutat mutat persze lehet tag:
struct pelda{ char *szoveg; int (*hasonlit)(const char *, const char *);};

A struktratag azonostjnak egy struktrn bell kell egyedinek lennie, vagyis msik struktrban nyugodtan ltezhet ugyanilyen nev tag. A begyazott struktra ugyangy elrhet, mint a fjl hatskrben deklarlt, azaz a kvetkez plda helyes:
struct a{ int x; struct b{ int y; } v2; } v1; /* . . . */ struct a v3; struct b v4;

A begyazott struktra gyakran nvtelen:


struct struki{ struct { int x, y; } pont; int tipus; } v;

224

STRUKTRK S UNIK

10.3 Struktrk inicializlsa A struktrt konstans kifejezsekbl ll inicializtorlistval lthatjuk el kezd rtkkel. Az inicializtorlista elemek rtkt a struktratagok a deklarcibeli elhelyezkeds sorrendjben veszik fel:
struct struki { int i; char lanc[25]; double d; } s = {20, Jancsika, 3.14};

Pontostsunk mg nhny dolgot! Loklis lettartam struktrk esetn az inicializtor inicializtorlista, vagy kompatibilis struktra tpus egyszer kifejezs lehet:
struct struki s = {20, Juliska, 3.14}, s1 = s;

Loklis (auto) struktra persze akr ilyen tpus struktrt visszaad fggvny hvsval is inicializlhat:
struct struki fv(int, char *, double); struct struki s2=fv(2, Boszi, 1.4);

Ha a struktrnak struktra vagy tmb tagja is van, akkor azt egymsba gyazott { }-kel lehet inicializlni.
struct struki { int i; long darab[3]; double d; } s = { 20, { 1l, 2l, 3l}, 3.14};

Tudjuk, hogy az inicializtorlista elemeinek szma nem haladhatja meg az inicializland struktratagok szmt! Ha az inicializtorlista kevesebb elem, mint az inicializland objektumok szma, akkor a maradk struktratagok a statikus lettartam implicit kezdrtk ads szablyai szerint tlti fel a fordt, azaz nullzza:
struct struki{ int cipomeret, /* magassag; /* char nev[26]; /* char cim[40]; /* s.cipomeret==42 */ s.magassag==180 */ az s.nev Magas Lajos */ s.cim res karakterlnc () kezdrtk. */ double fizetes;/* s.fizetes==0.0. */ } s = { 42, 180, Magas Lajos};

Nvtelen bitmez tag nem inicializlhat! Ha az inicializtorlistban nincs begyazott inicializtorlista, akkor az ott felsorolt rtkek az alaggregtumok, s ket a deklarci sorrendjben veszik fel az aggregtum elemei. Kapcsos zrjelek ugyanakkor akr

C programnyelv

225

az egyes inicializtorok kr is tehetk, de ha a fordtt nem kvnjuk "becsapni", akkor clszer ket az aggregtum szerkezett pontosan kvetve hasznlni!
typedef struct { int n1, n2, n3; } triplet; triplet nlist1[2][3] = { /* Helyes megolds: */ {{11, 12, 13}, {4, 5, 6}, {7, 18, 9}},/* Els sor. */ {{1, 2, 3}, {14, 15, 16}, {7, 8, 9}} /* 2. sor. */ }; triplet nlist2[2][3] = { /* Hibs megolds: */ {11, 12, 13}, {4, 5, 6}, {7, 18, 9}, /* Els sor. */ {1, 2, 3}, {14, 15, 16}, {7, 8, 9} /* 2. sor. */ };

A sizeof-ot struktrkra alkalmazva mindig teljes mretet kapunk akr a tpust adjuk meg operandusknt, akr az ilyen tpus objektumot. Pldul:
#include <stdio.h> struct st{ char *nev; /* A mutat mrete bjtban. */ short kor; /* + 2 bjt. */ double magassag; }; /* + 8 bjt. */ struct st St_Tomb[ ] = { {Jancsika, 18, 165.4}, /* St_Tomb[0] */ {Juliska, 116, 65.4}}; /* St_Tomb[1] */ int main(void){ printf(\nSt_Tomb elemeinek szma = %d\n, sizeof(St_Tomb)/sizeof(struct st); printf(\nSt_Tomb egy elemnek mrete = %d\n, sizeof(St_Tomb[0])); return 0; }

10.4 Struktratagok elrse A struktra s az unitagok elrshez ugyanazokat a tagelrs opertorokat alkalmazza a nyelv. A tagelrs opertort szelekcis opertornak, tagszelektornak is szoks nevezni. Prioritsuk magasabb az egyoperandusos mveleteknl, s kzvetlenl a () s a [] utn kvetkezik. Ktfajta tagelrs opertor van: az egyik a kzvetlen szelekcis opertor (.) s a msik a kzvetett (->). A kzvetlen tagelrs opertor alakja:
uttag-kifejezs.azonost

Az uttag-kifejezsnek struktra tpusnak, s az azonostnak e struktra tpus egy tagja nevnek kell lennie. A konstrukci tpusa az elrt tag tpusa, rtke az elrt tag rtke, s balrtk akkor s csak akkor, ha az uttagkifejezs az, s az azonost nem tmb.

226

STRUKTRK S UNIK

A kzvetett tagelrs opertor formja:


uttag-kifejezs->azonost

Az uttag-kifejezsnek struktra tpusra mutat mutatnak, s az azonostnak e struktra tpus egy tagja nevnek kell lennie. A konstrukci tpusa s rtke az elrt tag tpusa s rtke. Balrtk, ha az elrt tag nem tmb. Feltve, hogy s struct S tpus struktra objektum, s sptr struct S tpus struktrra mutat mutat, akkor ha t az struct S struktrban deklarlt, tpus tpus tag, az
s.t

s az
sptr->t

kifejezsek tpusa tpus, s mindkett a struct S struktra t tagjt ri el. A kvetkezk pedig szinonimk, ill. azt is mondhatjuk, hogy a > szelekci opertoros kifejezs a msik rvidtse:
sptr->t (*sptr).t

Az s.t s az sptr->t balrtkek, feltve, hogy t nem tmb tpus. Pldul:


struct S{ int t; char lanc[23]; double d; } s, *sptr = &s, Stomb[20]={ { 0, nulla, 0.}, { 1, egy, 1.}}; /* . . . */ s.t = 3; sptr->d = 4.56;

Az Stomb 20 elem, struct S struktrbl ll struktratmb, melynek els (Stomb[0]) s msodik (Stomb[1]) elemt kivve nincs explicit kezdrtke, azaz Stomb[2], . . ., Stomb[19] { 0, , 0,} rtk. A kvetkez pldk a struktratmb tagelrst szemlltetik:
Stomb[3].t=3; strcpy(Stomb[3].lanc, hrom); Stomb[3].d=3.3;

vagy:
sptr=Stomb+5; sptr->t=5; strcpy(sptr->lanc, t); sptr->d=5.5; /* Stomb[5].t */ /* Stomb[5].lanc */ /* Stomb[5].d */

C programnyelv

227

Ha struct B struktrnak van struct A struktra tpus tagja, akkor az ilyen struct A tagokat csak a tagszelekcis opertorok ktszeri alkalmazsval lehet elrni:
struct A { int j; double x; }; struct B { int i; char *nev; struct A a; double d; } s, *sptr = &s; /* . . . */ s.i = 3; s.a.j = 2; sptr->d = 3.14; (sptr->a).x = 6.28;

A szelekcis opertorok balrl jobbra ktnek, teht a kvetkezk teljesen azonosak:


(sptr->a).x sptr->a.x; (s.a).x s.a.x;

Emltettk mr, hogy a tagszelektorok prioritsa magasabb az egyoperandusos mveleteknl. Ezrt a


++sptr->i ++(sptr->i)

, azaz a kifejezs struct B i tagjt inkrementlja, s nem sptrt. Ha mgis ezt szeretnnk (br a konkrt pldnl ennek semmi rtelme sincs), akkor a ++sptr kifejezs rszt zrjelbe kell tenni, azaz:
(++sptr)->i

A
++(sptr++)->i

ugyancsak s.it inkrementlja, de a kifejezs mellkhatsaknt sptr is megn eggyel. Ha a zrjeleket elhagyjuk, persze akkor is idejutunk:
++sptr++->i ++(sptr++)->i

Ugyangy a nev cmen lev karaktert ri el a


*sptr->nev

, hisz az indirekcit az elbbiekbl kvetkezleg az sptrrel elrt nev cmen hajtja vgre a fordt. Vegyk mg a
*sptr++->nev++

228

STRUKTRK S UNIK kifejezst, aminek ugyanaz az rtke, mint az elz kifejezs, de mellkhatsknt sptr s nev inkrementlsa is megtrtnik.

Ha mr mindig hozzrendelsi pldkat hoztunk, akkor itt kell megemltennk, hogy struktrkat csak akkor lehet egymshoz rendelni, ha a forrs s a cl struktra azonos tpus.
struct A { int i, j; double d; } struct B { int i, j; double d; } /* . . . */ a = a1; /* a = b; /* a.i = b.i; /* a.j = b.j; a.d = b.d; a, a1; b; OK, a hozzrendels megy tagrl-tagra. */ HIBS, mert eltr a kt struktra tpusa.*/ Tagrl-tagra persze most is megy a dolog. */

Vegynk valamilyen pldt a struktratmbkre! Keressk meg a megadott, skbeli pontok kzt a kt, egymstl legtvolabbit! A pontok szma csak futs kzben dl el (n), de nem lehetnek tbben egy fordtsi idben vltoztathat rtknl (N). Az x koordinta bevitelekor res sort megadva, a bemenet elbb is befejezhet, de legalbb kt pont elvrand! Az input ellenrzend, s minden hibs rtk helyett azonnal jat kell krni. Az eredmny kzlsekor meg kell jelentetni a kt pont indexeit, koordintit s persze a tvolsgot is.
/* PELDA28.C: A kt, egymstl legtvolabbi pont megkeresse. */ #include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <math.h> #define INP 28 /* Az input puffer mrete. */ #define N 128 /* Pontok maximlis szma. */

A feladatot skbeli pontot ler struktra segtsgvel fogjuk megoldani:


struct Pont{ /* A Pont struktra. */ double x, y; }; int getline(char s[],int n){ /* . . . */ } int lebege(char s[]){ /* . . . */ }

A kt fggvny forrsszvege idemsoland!


int main(void){ char sor[INP+1]; /* Input puffer. */

A struktratmb defincija:

C programnyelv

229

struct Pont p[N]; /* Struktratmb. */ int n=0; /* Pontok szma. */ double max=-1., d; /* Pillanatnyi maximum s */ int i, j, tavi, tavj; /* segdvltozk. */ printf("A kt egymstl legtvolabbi pont a skban.\n" "Adja meg a pontok koordintaprjait rendre!\n" "Vge: res sor az X koordinta megadsnl.\n\n"); for(n=0; n<N; ++n){ printf("A(z) %d pont koordinti:\n", n+1); if(printf("X: "), getline(sor, INP)<=0) break; if(lebege(sor)) p[n].x=atof(sor); else { --n; continue;} while(printf("Y: "), getline(sor, INP), !lebege(sor)); p[n].y=atof(sor); } if(n<2){ printf("Legalbb kt pontot meg kne adni!\n"); return(1);} for(i=0; i<n-1; ++i) for(j=i+1; j<n; ++j)

Kitnen ltszik, hogyan kell elrni a struktra tmbelem tagjait!


if((d=sqrt((p[j].x-p[i].x)*(p[j].x-p[i].x)+ (p[j].y-p[i].y)*(p[j].y-p[i].y))) > max){ max=d; tavi=i; tavj=j; } printf( "A maximlis tvolsg kt pont:\n" "P[%d]: (%10.1f, %10.1f) s\n" "P[%d]: (%10.1f, %10.1f),\n" "s a tvolsg: %15.2f\n", tavi, p[tavi].x, p[tavi].y, tavj, p[tavj].x, p[tavj].y, max); return(0); }

Megoldand feladatok: Ha fokozni kvnja a feladatot, akkor Dolgozzon trbeli pontokkal! Rendezze a pontokat az origtl val tvolsguk cskken sorrendjben, s jelentesse meg a pontokat s a tvolsgot fejlccel elltva, tblzatosan s lapozhatan! Esetleg oldja meg, hogy ne lehessen ktszer ugyanazt a pontot megadni!

230

STRUKTRK S UNIK

10.5 Struktrk s fggvnyek Emltettk mr, hogy a struktra msolhat, hozzrendelhet, elrhetk a tagjai, kpezhet a cme, ill. tmb is elllthat belle, de fggvny is visszaadhat struktrt vagy erre mutat mutatt. Az fv1 visszaadott rtke struct struki struktra.
struct struki fv1(void);

Az fv2 viszont struct struki struktrra mutat mutatt szolgltat.


struct struki *fv2(void);

A fggvny paramtere is lehet struktra e kt mdon. Az fv3 struct struki struktrt fogad paramterknt.
void fv3(struct struki s);

Az fv4 viszont struct struki struktrra mutat mutatt fogad.


void fv4(struct struki *sp);

A kvetkez plda a helytelen gyakorlatot szemllteti. A fggvny paramterei s visszaadott rtke egyarnt struktra. Struktra persze akrmekkora is elkpzelhet.
typedef struct{ char nev[20]; int az; long oszt; } STUDENT; STUDENT strurend(STUDENT a, STUDENT b){ return((a.az < b.az) ? a : b); } /* . . . */ STUDENT a, b, c; /* . . . */ c = strurend(a, b);

Amg a strurend fut, hat darab STUDENT struktra ltezik: a, b, c, aztn a s b msolata s a fggvny visszaadott rtke a veremben. Clszer teht nem a struktrt, hanem arra mutat mutatt tadni a fggvnynek, ill. vissza is kapni tle, ha lehet, azaz:
STUDENT *strurnd(STUDENT *a, STUDENT *b){ return((a>az < b->az) ? a : b); } /* . . . */ STUDENT *z; /* . . . */ z = strurnd(&a, &b);

Prototpus hatskre ellenre a benne megadott struct hatskre globlis, azaz figyelmeztet zenet nlkl nem hvhatjuk meg a kvetkez fggvnyt:

C programnyelv
void fv(struct S *);

231

A problma elhrtshoz deklarlni vagy definilni kell a struktrt prototpus elrsa eltt:
struct S; /* . . . */ void fv(struct S *);

Ksztsnk struktrt s kezel fggvnycsaldot dtumok manipullsra! A datum struktrban nyilvntartjuk a dtum vt (ev), hnapjt (ho), napjt (nap), a Krisztus szletse ta a dtumig eltelt napok szmt: az n. dtumsorszmot (datumssz), a dtum karakterlnc alakjt (datumlanc) s azt, hogy a dtum az v hnyadik napja (evnap). Az egszet gy kpzeljk el, hogy vagy megadjk a dtumot v, h s nap alakban, s a DatumEHN fggvnnyel meghatrozzuk a struktra sszes tbbi adatt (a mainben d1 objektum gy kap rtket), vagy karakterlnc alak dtumbl a DatumKARral lltjuk el a datum struktra tagjainak rtkeit (a fprogramban d2 e mdon jut rtkhez). Mindkt struct datum objektumnak rtket ad rutin vgl dtumellenrzst vgez a Datume fggvnnyel, s ezt a logikai rtket szolgltatja. A NapNev visszaadja a dtum hten belli napjnak nevt. Pontosabban a nv karakterlncnak cmt. A tovbbi rutinok mveleteket vgeznek a dtum struktrkkal. A DatumKul megllaptja kt dtum klnbsgt, s szolgltatja ezt a napszmot. A DatumMegEgy inkrementlja, s visszaadja a dtum objektumot. A DatumMegint pozitv, egsz rtket ad hozz. A dtumfggvnyek mind a MINDATUM s MAXDATUM kztti tartomnyban dolgoznak. A main ki is prblja az sszes dtumfggvnyt. Figyeljk meg, hogy mindegyik fggvny struct datum objektumra mutat paramterknt kapja meg a manipullt dtum struktr(ka)t! A DatumMegEgy s a DatumMegint visszatrsi rtke struct datum struktra.

232

STRUKTRK S UNIK

Fedezzk fel, hogy a globlis MAXDATUM, a hnapi napszmokat tartalmaz honap tmb, s a Datume fggvny static trolsi osztlya miatt loklis a DATUM.C modulra! Nincs is prototpus a Datumere a DATUM.H fejfjlban. Az rtkket nem vltoztat, de csak a rutin blokkjbl elrend oszto vltoz, s a napnv karakterlncokra mutatkbl ll hetnev mutattmb statikus lettartamak, de hatskrk loklis. Vegyk mg szre a DATUM.Hban, hogy a _DATUMH makr egyetlen struct datum defincit tesz lehetv akkor is, ha a fordtsi egysgben tbbszr kapcsolnk be a fejfjlt.
/* DATUM.H: Dtumok kezelse. */ #include <stdio.h> #include <ctype.h> #include <stdlib.h> #include <string.h> #if !defined(_DATUMH) #define _DATUMH struct datum{ int ev, ho, nap, evnap; long datumssz; /* Dtumsorszm. */ char datumlanc[11]; }; #endif const char *NapNev(struct datum *); int DatumEHN(int, int, int, struct datum *); int DatumKAR(const char *, struct datum *); long DatumKul(struct datum *, struct datum *); struct datum DatumMegEgy(struct datum *); struct datum DatumMegint(struct datum *, int); /* DATUM.C: Dtumok kezelse. */ #include "DATUM.H" #define MINDATUM 366 static const long MAXDATUM=9999*365l + 9999/4 9999/100+9999/400; static int honap[]={0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; const char *NapNev(struct datum *pd){ static char *hetnev[]={"vasrnap", "htf", "kedd", "szerda", "cstrtk","pntek","szombat"}; return hetnev[(pd->datumssz)%7l]; }

A dtumsorszmbl 7tel kpzett modulus alapjn llaptja meg a hten belli napindexet a NapNev rutin.
static int Datume(struct datum *pd){ int i; honap[2]=28+(!(pd->ev%4)&&pd->ev%100||(!pd->ev%400)); if(pd->ev<1 || pd->ev>9999 || pd->ho<1 || pd->ho>12 || pd->nap<1 || pd->nap>honap[pd->ho]){

C programnyelv
pd->evnap=0; pd->datumssz=0l; return 0;} else { sprintf(pd->datumlanc, "%04d.%02d.%02d", pd->ev, pd->ho, pd->nap); pd->evnap=pd->nap; for(i=1; i<pd->ho; ++i)pd->evnap+=honap[i]; pd->datumssz=(pd->ev-1)*365l+pd->evnap+ pd->ev/4-pd->ev/100+pd->ev/400; return 1; } }

233

A Datume ugyangy a dtumot ellenrzi, s logikai vlaszt ad a formlisan je a dtum? krdsre, mint a korbbi datume fggvnyek. Nem karakterlncbl dolgozik azonban, hanem a struktra ev, ho, nap tagjaibl, melyeket a DatumEHN, ill. a DatumKAR ksztettek oda. Ha hibs a dtum, akkor nullzza a rutin az evnap s a datumssz tagokat. Ha j a dtum, akkor a Datume kpezi a datumlancba a dtum karakterlnc alakjt, s meghatrozza az evnap s a datumssz rtkt. Az evnap a dtum napszmrl indul, s a rutin hozzadogatja a megelz hnapok maximlis napszmait. A dtumsorszm megllaptshoz a szkv vizsglathoz hasznlatos kifejezst alkalmazza a fggvny. Az STDIO.H bekapcsolsval rendelkezsre ll sprintf ugyangy mkdik, mint printf trsa, de nem a szabvny kimenetre, hanem az els paramtereknt kapott karaktertmbbe dolgozik. A formtumspecifikcikban a mezszlessg eltt ll 0 hatsra a jobbra igaztott szmok balrl nem szkz, hanem 0 feltltst kapnak. Magyarn a 932.2.3. dtumbl 0900.02.03 karakterlnc lesz. Szltunk mr rla, hogy a Datume nem hvhat ms forrsmodulbl. A DATUM.Cben is csak a DatumEHN s a DatumKAR idzi meg utols lpseknt.
int DatumEHN(int e, int h, int n, struct datum *pd){ pd->ev=e; pd->ho=h; pd->nap=n; if(e>=0&&h>=0&&n>=0&&e<10000&&h<100&&n<100) sprintf(pd->datumlanc, "%04d.%02d.%02d", e, h, n); else *pd->datumlanc=0; return Datume(pd);} int DatumKAR(const char *lanc, struct datum *pd){ pd->ho=pd->nap=0; strncpy(pd->datumlanc, lanc, 10); pd->datumlanc[10] = 0; pd->ev=atoi(lanc); while(isdigit(*lanc))++lanc; if(*lanc!=0){

234

STRUKTRK S UNIK

++lanc; pd->ho=atoi(lanc); while(isdigit(*lanc))++lanc; if(*lanc){ ++lanc; pd->nap=atoi(lanc);}} return Datume(pd); }

A DatumEHN a kapott, int tpus v, h, nap segtsgvel tlti fel az utols paramterknt elrt dtum struktrt. Az sprintf hvs eltti vizsglatra azrt van szksg, hogy a rutin ne tudja tlrni a 11 elem karaktertmb, datumlanc tagot a memriban valamilyen egszen zldsg v, h, nap paramter miatt. Ilyenkor res lesz a datumlanc. A DatumKAR tmsolja a karakterlnc alakban kapott dtum els 10 karaktert a datumlancba. Nullzza a honapot s napot. Ltszik, hogy a fggvny nem kti meg olyan szigoran sem az v, sem a hnap s nap jegyszmt, mint a korbbi datume, ill. elvlaszt karakterknt csak valamilyen nem numerikust vr el. A karakterlnc egssz konvertlt elejt vnek, az els elvlaszt karakter utni rszt hnapnak, s a msodik elvlaszt karakter mgttieket napnak tekinti a rutin, hacsak idkzben vge nem lesz a karakterlncnak. Vgl mindkt fggvny meghvja a Datumet, s ennek visszatrsi rtkt szolgltatja.
long DatumKul(struct datum *pd1, struct datum *pd2){ if(pd1->datumssz>pd2->datumssz) return pd1->datumssz-pd2->datumssz; else return pd2->datumssz-pd1->datumssz; }

A DatumKul kpzi a kt paramter dtum struktra dtumsorszm tagjainak klnbsge abszolt rtkt.
struct datum DatumMegEgy(struct datum *pd){ int e=pd->ev, h=pd->ho, n=pd->nap; struct datum d; honap[2]=28+(e%4==0 && e%100 || e%400==0); if(++n>honap[h]){ n=1; ++h; if(h>12){ h=1; ++e; }} if(!DatumEHN(e, h, n, &d)) d=*pd; return d; }

C programnyelv

235

A paramtere dtumot inkrementl DatumMegEgy munkavltozkba rakja az vet, a hnapot s a napot. Meghatrozza az v szerinti februr pontos napszmt. Nveli eggyel a napot. Ha ez tlmenne a hnap szerinti maximlis napszmon, akkor 1 lesz, s a hnapszm nvelse jn. Ha ez 13 lenne, akkor 1 lesz, s az vszm nvelse kvetkezik. A megllaptott, j v, h, nap alapjn a DatumEHN feltlti a loklis, d dtum objektumot. Ha az j dtum rvnytelen volt, akkor a vltozatlansgot jelzend a rutin hozzrendeli dhez a paramter cmen lev, eredeti dtumot. A hozzrendels a paramter cmen lev (ezrt kell el az indirekci) struct datum minden tagjt egy az egyben tmsolja a balrtk, d, loklis dtum objektum tagjaiba rendre. A visszatrs sorn a DatumMegEgy ltrehoz a veremben egy ideiglenes dtum objektumot, melybe tagrltagra bemsolja a d loklis dtum vltozt. Visszatrs utn aztn a main hozzrendeli az ideiglenes dtum objektumot a mainben loklis dhez.
struct datum DatumMegint(struct datum *pd, int np){ int e=pd->ev, h=pd->ho, n=pd->nap; long dpd = pd->datumssz + np + 365; /* A tiszta j konstans 365.24223 lenne kzi szmts szerint!!!! */ static double oszto=365.24225; struct datum d=*pd; if(np <= 0) return d; if(dpd > MAXDATUM){ e=9999; h=12; n=31; } else { e= (int)dpd/oszto; n=dpd-e*365l-e/4+e/100-e/400; honap[2]=28+(e%4==0 && e%100 || e%400==0); for(h=1; n > honap[h]; ++h)n-=honap[h]; } if(!DatumEHN(e, h, n, &d)) d=*pd; return d; }

Csak a kezdett s a vgt tekintve a pozitv napszmot a dtumhoz ad DatumMegint a DatumMegEgygyel megegyezen dolgozik. Ltszik, hogy negatv, hozzadand napszmot, vagy a mvelet vgn rvnytelen dtumot kapva, az eredeti dtum objektumot szolgltatja a rutin vltozatlanul. A dtumsorszmot megnveli a napszmmal s mg 365tel. Ha gy meghaladn a 9999.12.31et, akkor ezt adn vissza. Ha nem, akkor az j

236

STRUKTRK S UNIK

dtumsorszmot elosztja a tapasztalati alapon a [MINDATUM, MAXDATUM] tartomnyban rvnyes venknti tlagos napszmmal, s ez lesz az j vszm. Visszaszmolja belle az j v pontos napszmt, s a kt rtk klnbsgbl hnap s napszmot kpez.
/* PELDA29.C: A dtumok kezelsnek kiprblsa. */ #include "DATUM.H" void main(void) { long kul; struct datum d1, d2, d; printf("Dtum mveletek:\n\n"); DatumEHN(2003, 12, 31, &d1); DatumKAR("2003-2-13", &d2); printf("A(z) %s. s a(z) %s. klnbsge %ld nap!\n", d1.datumlanc, d2.datumlanc, (kul=DatumKul(&d1, &d2))); d=DatumMegEgy(&d1); printf("A(z) %s. + 1 a(z) %s.\n", d1.datumlanc, d.datumlanc); d=DatumMegint(&d2, (int)kul); printf("A(z) %s. + %ld a(z) %s.\n", d2.datumlanc, kul, d.datumlanc); printf("A(z) %s. %s.\n", d2.datumlanc, NapNev(&d2)); }

Megoldand feladatok: Bvtse a DATUM.H s DATUM.C fjlokat a kvetkez funkcikat ellt fggvnyekkel! Persze prblja is ki ket! A hnapnv karakterlnc ellltsa a hnapszm alapjn. Olyan karakterlnc alak dtum ltrehozsa, melyben a hnap megnevezse szerepel a hnap szma helyett. A DatumMegint olyan trsa, hogy a napszm paramter negatv is lehessen. Ksztsen ugyanilyen szellemben struktrt s kezel fggvnycsaldot az idre is! 10.6 nhivatkoz struktrk s dinamikus adatszerkezetek Tudjuk, hogy a struktrnak nem lehet void, nem teljes s fggvny tpus tagja, de nem lehet tag

C programnyelv az ppen definci alatt ll struktra

237

sem. Lehet viszont tag nem teljes tpus struktrra, gy akr a definci alatt llra, mutat mutat. Azt a struktrt, melynek legalbb egy nmagra mutat tagja van, nhivatkoz struktrnak nevezik. A dinamikus adatszerkezeteket [3]: listkat, fkat stb. ler adatkonstrukcik a Cben nhivatkoz struktrk. Nzznk nhnyat! Egyirny listhoz pldul a kvetekez struktra lenne hasznlhat:
struct List1{ ADAT adat; struct List1 *kov; };

, ahol az ADAT tpus adat tagon valamilyen, a lista egy elemben troland adatokat ler struktrt kell rteni. A kov az egyirny lista kvetkez struct List1 tpus elemre mutat, ill. a lista vgt NULL mutat jelzi. A lista kezelhetsghez ezen tl mr csak egy horgonypontra, s esetleg egy seged mutatra
struct List1 *KezdoPont = NULL, *seged;

van szksg a listt manipull programban. Tegyk fel, hogy ismertek az ADATok, s vegyk fel a lista els elemt!
if(!(KezdoPont=seged=(struct List1*)malloc( sizeof(struct List1)))){ printf("Elfogyott a memria!\n"); exit(1); } else{ /* seged->adat vegye fel az ADATok rtkt! */ seged->kov=NULL; }

A lista kvetkez eleme:


if(!(seged->kov=(struct List1*)malloc( sizeof(struct List1)))){ printf("Elfogyott a memria!\n"); exit(1); } else{ /* seged->adat vegye fel az ADATok rtkt! */ seged->kov=NULL; } /* . . . */

Elg! rjunk Beszur1 fggvnyt, mely paramterknt megkapja a beszrand ADATokat, s annak a listaelemnek a cmt, mely utnra az j listaelem kell, hogy kerljn! Ha mg nincs is lista, akkor ezen a pozcin

238

STRUKTRK S UNIK

kapjon NULL mutatt a rutin! A visszatrsi rtk legyen a most ltestett listaelem cme, ill. NULL mutat, ha elfogyott a memria!
struct List1 *BeSzur1(ADAT a, struct List1 *elozo){ struct List1 *p; if(p=(struct List1 *)malloc(sizeof(struct List1))){ p->adat=a; if(elozo){ p->kov=elozo->kov; elozo->kov=p; } else p->kov=NULL; } return p; }

Ekkor a lista ltrehozsa a kvetkez:


KezdoPont=seged=BeSzur1(adatok, NULL); while(/* Vannak kvetkez adatok? */&&seged!=NULL) seged=BeSzur1(adatok, seged); if(!seged){ printf("Elfogyott a memria!\n"); exit(1); }

A ltrehozott egyirny lista felhasznls utn a kvetkez kddal semmisthet meg:


while(KezdoPont){ seged=KezdoPont->kov; free(KezdoPont); KezdoPont=seged; }

Az egyirny listban lehet j elemet brhov beszrni (BeSzur1), brhonnt trlni, de a listt ahogyan a megsemmist kd is mutatja csak elrehaladva lehet elrni, visszafel lpkedve nem. Az oda visszahaladshoz ktirny lista kell:
struct List2{ ADAT adat; struct List2 *kov, *elo; }; struct List2 *KezdoPont = NULL, *seged;

A visszalpegets lehetsgt a megelz listaelemre mutat, elo mutattag biztostja. A lista vgt mindkt irnyban NULL mutat jelzi. A beszrs:
struct List2 *BeSzur2(ADAT a, struct List2 *elozo){ struct List2 *p; if(p=(struct List2 *)malloc(sizeof(struct List2))){ p->adat=a; if(elozo){ p->elo=elozo; p->kov=elozo->kov; elozo->kov=p;

C programnyelv
if(p->kov) p->kov->elo=p; } else p->elo=p->kov=NULL; } return p; }

239

A BeSzur2 csak egyet nem tud: a ltez els elem el beszrni. Ezen gy segthetnk:
seged=BeSzur2(adatok, NULL); seged->kov=KezdoPont; KezdoPont=seged;

A trls:
struct List2 *Torol2(struct List2 *ezt){ struct List2 *p=NULL; if(ezt){ p=ezt->kov; if(ezt->elo) ezt->elo->kov=ezt->kov; if(ezt->kov) ezt->kov->elo=ezt->elo; free(ezt); } return p; }

A Torol2 fggvnynek is csak a ltez, legels elem trlsekor kell segteni, hisz vltozik a
KezdoPont=Torol2(KezdoPont);

Felhasznls utn a ktirny lista is ugyangy semmisthet meg, mint az egyirny. A fk kzl vlasszuk ki a binris keresft! Ennek pontjaiban legfeljebb kett az elgazsok szma, s a pontokban helyet foglal struktrk ADAT rsze alapjn a fa, mondjuk, nvekvleg rendezett. Ltezik teht egy
int Hasonlit(ADAT a1, ADAT a2);

rutin, mely zrust szolgltat, ha a1==a2, pozitv rtket, ha a1>a2, ill. negatvat egybknt. A binris keresfban a mindenkori aktulis pont bal gn lev pontok kzl egy sem nagyobb, s a jobb gn helyet foglalk kzl viszont egyik sem kisebb az aktulis pontnl. Az adatstruktra:
struct BinFa{ ADAT adat; struct BinFa *bal, *jobb; }; struct BinFa *KezdoPont = NULL;

A beszrst vgz rekurzv fggvny a kvetkez:


struct BinFa *BeSzurBF(ADAT a, struct BinFa *p){ int fel; if(!p){ /* j pont kszl. */ if(p=(struct BinFa *)malloc(sizeof(struct BinFa))){

240

STRUKTRK S UNIK

p->adat=a; p->bal=p->jobb=NULL; } else printf("Elfogyott a memria!\n"); } else if((fel=Hasonlit(a, p->adat))==0) /* Volt mr ilyen ADAT, s itt ez a kd van. */ else if(fel<0) /* A bal oldali rszfba val. */ p->bal=BeSzurBF(a, p->bal); else /* A jobb oldali rszfba val. */ p->jobb=BeSzurBF(a, p->jobb); return(p); }

A kvetkez rekurzv fggvny nvekvleg rendezett sorrendben vgez el minden ponton valamilyen tevkenysget:
void Tevekeny(struct BinFa *p){ if(p){ Tevekeny(p->bal); /* Itt van a tevkenysg kdja. */ Tevekeny(p->jobb); } }

Keressk meg egy szvegfjlban a benne elfordul szavakat! llaptsuk meg ezen kvl, hogy a szavak a szvegfjl mely sorszm soraiban fordulnak el! Ha ugyanaz a sz egy sorban tbbszr is megtallhat, a sor sorszmt ekkor is csak egyszer kell kzlni. Vgl kzlendk a szavak, s az elfordulsi sor sorszmok nvsorban! A megoldsban a szavak trolshoz binris keresft hasznlunk, s a szhoz tartoz elfordulsi sor sorszmokat minden ponthoz tartozan egyirny listban tartjuk nyilvn. A PELDA30 programot a parancssorbl
PELDA30 < PELDA30.C > EREDMENY.TXT

mdon indthatjuk, s a kszlt lista az EREDMENY.TXT fjlban tanulmnyozhat.


/* PELDA30.C: Kirja a szvegben elfordul szavak listjt s megadja azt is, hogy a szavak milyen sorszm sorokban fordultak el! */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <ctype.h> #define MAXSOR 256 /* A beolvasott sor max. hossza. */ /* A sorok sorszmainak egyirny listja: */ struct List1{ int sorsz; struct List1 *kov; }; /* A binris keres fa: */ struct BinFa{ /* Alapcsompont. */

C programnyelv
char *szo; /* A szra mutat. */ struct List1 *sorok; /* A sorok sorszmai. */ struct BinFa *bal; /* A bal oldali g. */ struct BinFa *jobb;}; /* A jobb oldali g. */ /* Fggvny prototpusok: */ struct BinFa *BeSzurBF(char *, int, struct BinFa *); void SorMeg(struct BinFa *, int); void Kiir(struct BinFa *); int main(void){ static char szoelv[]=" \t\n\r\f\"\'\\\a\?" ":;,.()[]{}*/%+-&|^~!<>=#"; struct BinFa *KezdoPont=NULL; char sor[MAXSOR], *szo; int sorszam=1; printf("Szavak keresztreferencija szvegben:\n"); while(sor[MAXSOR-1]=2, fgets(sor, MAXSOR, stdin)){ if(!sor[MAXSOR-1]&&sor[MAXSOR-2]!='\n') printf("Lehet kis elsorszmozs!\n"); szo=strtok(sor, szoelv); while(szo){ KezdoPont=BeSzurBF(szo, sorszam, KezdoPont); szo=strtok(NULL, szoelv); } ++sorszam; } Kiir(KezdoPont); return 0; }

241

A fgets fggvny a getlinehoz nagyon hasonlan dolgozik. Karakterlncot olvas be a szabvny bemenetrl (stdin), melyet az els paramter cmtl kezdve letrol a memriban. Az olvass lell, ha a fggvny a msodik paramternl eggyel kevesebb (MAXSOR 1), vagy \n karaktert olvasott. A getlinetl eltren azonban a rutin a \nt is elhelyezi a lncban, s a lnc vghez mg egy \0t is hozzilleszt. Sikeres esetben az fgets az els paramter mutatval tr vissza. Fjlvgen, vagy hiba esetn viszont NULLt kapunk tle. Ha a sor tmb utols pozcijn van a lnczr zrus, de eltte nincs ott a soremels karakter, akkor az aktulisan olvasott sor nem frt el egy menetben a bemeneti pufferben, s ebbl kvetkezleg elsorszmozs trtnik. Az strtok lersa megtallhat a MUTATK szakasz Karakterlnc kezel fggvnyek fejezetben!
struct BinFa *BeSzurBF(char *a,int sorszam,struct BinFa *p) { int fel; if(!p){ /* j sz rkezett */ p=(struct BinFa *)malloc(sizeof(struct BinFa)); if(p&&(p->szo=(char *)malloc(strlen(a)+1))){ strcpy(p->szo, a);

242

STRUKTRK S UNIK

if(p->sorok=(struct List1 *)malloc( sizeof(struct List1))){ p->sorok->sorsz=sorszam; p->sorok->kov=NULL; } p->bal=p->jobb=NULL; } if(!p||!p->szo||!p->sorok){ printf("Elfogyott a memria!\n"); exit(1); } } else if((fel=strcmp(a, p->szo))==0) SorMeg(p, sorszam); else if(fel<0) p->bal=BeSzurBF(a, sorszam, p->bal); else p->jobb=BeSzurBF(a, sorszam, p->jobb); return(p); } /* Sorszm hozzadsa az egyirny lista vghez: */ void SorMeg(struct BinFa *p, int sorszam){ struct List1 *seged=p->sorok; while(seged->kov!=NULL && seged->sorsz!=sorszam) seged=seged->kov; if(seged->sorsz!=sorszam){ if(seged->kov=(struct List1 *)malloc( sizeof(struct List1))){ seged->kov->sorsz=sorszam; seged->kov->kov=NULL; } else{ printf("Elfogyott a memria!\n"); exit(1); } } } /* A fa kirsa: */ void Kiir(struct BinFa *p){ struct List1 *seged; int i; if(p){ Kiir(p->bal); printf("%s:\n", p->szo); for(seged=p->sorok, i=0; seged; seged=seged->kov, ++i) printf("%7d|", seged->sorsz); if(i%10!=9) printf("\n"); Kiir(p->jobb); } }

Megoldand feladatok: Fejlessze tovbb a PELDA30.Cben megoldott feladatot a kvetkezkpp, s persze prblja is ki! C parancssori paramterrel indtva a program ne gyjtse a szabvnyos C kulcsszavak elfordulsait. Ha a szabvny kimenet (stdout) nem fjl, akkor bontsa lapokra a listt a szoftver.

C programnyelv

243

N parancssori paramterrel startolva jelentesse meg a program megsorszmozva, de egybknt vltozatlanul a bemenetet. 10.7 Struktra trillesztse A fordt a struktratagokat deklarcijuk sorrendjben nvekv memria cmeken helyezi el. Minden adatobjektum rendelkezik trillesztsi ignnyel is. A fordt olyan eltolssal helyezi el az adatobjektumot, hogy az
eltols % trillesztsi-igny == 0

zrus legyen. Struktrk esetn ez a szably a tagok elhelyezsre vonatkozik. Ha pldnak vesszk a
struct struki { int i; char lanc[3]; double d; } s;

struktrt, akkor tudjuk, hogy az s objektumot nvekv memria cmeken gy helyezi el a fordt, hogy 1. ngy bjtot (32 bites esetben) foglal az int tagnak, 2. aztn a 3 bjtos karakterlnc kvetkezik, s 3. vgl 8 bjtot rezervl a double taghoz. Bizonyos fordt opcik, vagy valamilyen #pragma direktva segtsgvel vezrelhetjk a struktra adatok memriabeli illeszkedst. Ez azt jelenti, hogy az adatokat 1gyel, 2vel, 4gyel stb. maradk nlkl oszthat cmeken: bjthatron, szhatron, dupla szhatron stb. kell elhelyezni. Felkrjk az olvast, hogy nzzen utna a dolognak a programfejleszt rendszere segtsgben! Brhogyan is, eme belltsok hatsra a fordt minden struktratagot, az elst kveten, olyan hatron trol, mely megfelel a tag trillesztsi ignynek. A bjthatrra igazts azt jelenti, hogy a struktra objektum elhelyezse brmilyen cmen kezddhet, s a struktratagok ugyancsak brmilyen cmen elhelyezhetk tpusuktl fggetlenl. A plda s objektum sszesen 15 bjtot foglal el ilyenkor, s a memria trkp a kvetkez:

244 i 4 bjt lanc 3 bjt d 8 bjt

STRUKTRK S UNIK

A szhatrra igazts azt jelenti, hogy a struktra objektum kezdcme pros kell, hogy legyen, s a struktratagok - a char tpustl eltekintve - ugyancsak pros cmeken helyezkednek el. A plda s objektum gy sszesen 16 bjtot foglal el. Egy bjt elveszik, s a memria trkp a kvetkez: i 4 bjt lanc 3 bjt 1 b d 8 bjt

A dupla szhatrra igazts azt jelenti, hogy a struktra objektum elhelyezse nggyel maradk nlkl oszthat cmen (dupla szhatron) trtnik meg, a char tpus struktratagok bjthatron kezddnek, a short tpus tagok szhatron indulnak s a tbbi tpus tag dupla szhatron (nggyel maradk nlkl oszthat cmen) kezddik. Dupla szhatrra igaztva az s objektum megegyezik az elzvel. A hatrra igaztsi jtk folytathat rtelemszeren tovbb. Persze a struktrt nem ilyen butn definilva igaztstl fggetlenl elrhetjk, hogy egyetlen bjt elvesztse se kvetkezzk be:
struct struki { double d; int i; char lanc[3]; } s;

10.8 UNIK Az uni tpus a struktrbl szrmazik, de a tagok kztt zrus a cmeltols. Az uni azt biztostja, hogy ugyanazon a memria terleten tbb, klnfle tpus adatot trolhassunk. Az
union unio{ int i; double d; char t[5]; } u, *pu = &u, tu[23];

C programnyelv

245

definciban az u union unio tpus objektum, a pu ilyen tpus objektumra mutat mutat, s a tu egy 23 ilyen tpus elembl ll tmb azonostja. Az u objektum - pldul - egyazon memria terleten biztostja az i nev int, a d azonostj double s a t nev karaktertmb tpus tagjainak elhelyezst, azaz:
&u &u.i &u.d ...

Ennek szellemben aztn igaz, hogy az uni objektumra mutat mutat annak egyben minden tagjra is mutat.
(pu=&u) &u.i &u.d ...

Termszetesen a dolog csak a mutatk rtkre igaz, mert az &u (union unio *), az &u.i (int *) s az &u.d (double *), azaz tpusban eltrnek. Ha azonban unit megcmz mutatt explicit tpusmdostssal tagjra irnyul mutatv alaktjuk, akkor az eredmny mutat magra a tagra mutat:
u.d=3.14; printf(*(&u.d) = %f\n, *(double *)pu);

Az uni helyfoglalsa a trban akkora, hogy benne a legnagyobb bjtigny tagja is elfr, azaz:
sizeof(union unio) sizeof(u) 8.

Teht 4 bjt felhasznlatlan, ha int adatot tartunk benne, ill. 3 bjt elrhetetlen, ha karaktertmbt rakunk bele. Az uni egy idben csak egyetlen tagjt tartalmazhatja. Lttuk mr, hogy az unitagokat ugyanazokkal a tagszelektor opertorokkal rhetjk el, mint a struktratagokat:
u.d = 3.15; printf(u.d=%f\n, u.d); /* OK: u.d=3.15 jelenik meg. */ printf(u.i=%d\n, u.i); /* Furcsa eredmny szletik. */ printf(u.t[0]=%c\n, u.t[0]);/* Valami csak megjelenik, vagy sem. */ printf(u.t=%s\n, u.t); /* Csoda karakterlnc ltszik. Ki tudja, hol van a lnc vge!*/ strcpy(pu->t, Hoh); printf(u.t=%s\n, pu->t);/* OK: a Hoh ltszik. */ printf(u.i=%d\n, pu->i);/* Furcsa eredmny szletik. */ printf(u.d=%f\n, pu->d);/* Nagyon szortsunk, hogy ne legyen lebegpontos tl vagy alulcsorduls! */

246

STRUKTRK S UNIK

Valahonnan teht clszer tudni - pldul gy, hogy nyilvntartjuk milyen tpus adat is tallhat pillanatnyilag az uni objektumban, s azt szabad csak elrni. Ha egy uni tbbfle, de azonos kezd szerkezet struktrval indul, s az uni tartalma e struktrk egyike, akkor lehetsg van az uni kzs kezdeti rszre hivatkozni. Pldul:
union{ struct{ int tipus;} t; struct{ int tipus; int iadat;} ti; struct{ int tipus; double dadat;} td; /* . . . */ } u; /* . . . */ u.td.tipus = DOUBLE; u.td.dadat = 3.14; /* . . . */ if(u.t.tipus == DOUBLE) printf(%f\n, u.td.dadat); else if(u.t.tipus == INT) printf(%d\n, u.ti.iadat); else /* . . . */

Unikkal pontosan ugyanazok a mveletek vgezhetk, mint a struktrkkal. Hozzrendelhetk, msolhatk, hozzfrhetnk a tagjaikhoz, kpezhet a cmk, tadhatk fggvnyeknek s rutinok visszatrsi rtkei is lehetnek. 10.8.1 Unideklarcik Az ltalnos deklarcis szably azonos a struktrval. Az eltrsek a kvetkezk: Az unik tartalmazhatnak bitmezket. Mindegyik bitmez azonban az uni kezdettl indul, s gy kzlk csak egy lehet aktv. A kvetkez fejezetben trgyalt bitmezk gpfgg brzolsra itt is fel szeretnnk hvni kln a figyelmet! Az uni tagja nem lehet void, nem teljes, vagy fggvny tpus. Nem lehet a definci alatt ll uni egy pldnya, de ilyenre mutat mutat persze lehet. Unik esetben a deklarciban csak az elsnek deklarlt tagnak adhat explicit kezdrtk. Pldul:
union unika{ int i; double d; char t[6]; } u = { 24 };

C programnyelv

247

Csak az u.i kaphatott, s kapott is 24 kezdrtket. Ha kicsit bonyolultabb esetet nznk:


union{ char x[2][3]; int i, j ,k; } y = {{{'1'}, {'4'}}};

Az y uni vltoz inicializlsakor aggregtum inicializtort hasznlunk, mert az uni els tagja ktdimenzis tmb. Az 1 inicializtor a tmb els sorhoz tartozik, gy az y.x[0][0] felveszi az 1 rtket, s a sor tovbbi elemei tiszta zrusok lesznek az implicit kezdrtk ads szablyai szerint. A 4 a msodik sor els elemnek inicializtora, azaz y.x[1][0] = 4, y.x[1][1] = 0 s y.x[1][2] = 0. Loklis lettartam unik esetn az inicializtor kompatibilis uni tpus egyszer kifejezs is lehet:
union unika{ int i; double d; char t[6]; } u = { 24 }, u1 = u;

Az unideklarciban is elhagyhat az unicmke. Az unik elfordulhatnak struktrkban, tmbkben, s tmbk, ill. struktrk is lehetnek tagok unikban:
#define MERET 20 struct { char *nev; int adat; int u_tipus; /* Az uniban aktulisan trolt */ union{ /* tpus nyilvntartshoz. */ int i; float f; char *mutato; } u; } tomb[MERET];

Ilyenkor a tomb i-edik eleme i unitagjhoz val hozzfrs alakja:


tomb[i].u.i

s a mutato tag mutatta els karakter elrsnek formja:


*tomb[i].u.mutato

10.9 Bitmezk (bit fields) Bitmezk csak struktra vagy uni tagjaknt definilhatk, de a struktrban s az uniban akr keverten is elfordulhatnak bitmez s nem bit-

248

STRUKTRK S UNIK

mez tagok. A bitmez struktratag deklarcis szintaktikja kicsit eltr a norml tagoktl:
tpusspecifiktor <deklartor> : konstans-kifejezs;

, ahol a tpusspecifiktor csak signed int, unsigned int vagy int lehet az ANSI C szabvny szerint. Az int tulajdonkppen signed int. A deklartor a bitmez azonostja, mely el is maradhat. Ilyenkor a nvtelen bitmez specifiklta bitekre nem tudunk hivatkozni, s a bitek futsidej tartalma elre megjsolhatatlan. A konstans-kifejezs csak egszrtk lehet. Zrus s sizeof(int)*8 kzttinek kell lennie, s a bitmez szlessgt hatrozza meg. Bitmez csak struktra vagy uni tagjaknt deklarlhat. Nem kpezhet azonban bitmezk tmbje. Fggvnynek sem lehet visszaadott rtke a bitmez. Nem megengedett a bitmezre mutat mutat s tilos hivatkozni a bitmez tag cmre, azaz nem alkalmazhat r a cm (&) opertor sem. A bitmezk az int terleten (dupla szban, vagy szban) deklarcijuk sorrendjben az alacsonyabb helyirtk bitpozciktl a magasabbak fel haladva foglaljk el helyket. Az int pontos mrete, bitmezvel val feltltsnek szablyai s sorrendje a programfejleszt rendszertl fgg. Clszer teht a segtsgben utnanzni a dolognak. Maradjunk meg azonban az elz bekezdsben emltett szablynl, s a knnyebb szemlltethetsg vgett mg azt is ttelezzk fel, hogy az int 16 bites! Ilyenkor pldul a:
struct bitmezo{ int i: 2; unsigned j: 5; int : 4, k: 1; unsigned m: 4; } b, *pb = &b;

ltal elfoglalt sz bittrkpe a kvetkez: 15 14 13 12 11 10 9 m k 8 7 6 5 4 j 3 2 1 i 0

Ha az m bitmez tag szlessge 4-nl nagyobb lett volna, akkor j szt kezdett volna a fordt, s az elz sz fels ngy bitje kihasznlatlan ma-

C programnyelv

249

radt volna. ltalnossgban: a (dupla)szn tllg bitmez j (dupla)szt kezd, s az elz (dupla)szban a fels bitek kihasznlatlanok maradnak. Ha a deklarciban valamely (nvtelen) bitmeznl zrus szlessget adunk meg, akkor mestersgesen knyszertjk ki ezt a kvetkez (dupla) szhatrra llst. A bitmeznek elg szlesnek kell lennie ahhoz, hogy a rgztett bitminta elfrjen benne! Pldul a kvetkez tagdeklarcik illeglisak:
int alfa : 17; unsigned beta : 32

A bitmezk ugyanazokkal a tagszelektor opertorokkal (. s ->) rhetk el, mint a nem bitmez tagok:
b.i vagy pb->k

A bitmezk kis signed vagy unsigned egsz rtkekknt viselkednek (rgtn tesnek az egszellptetsen), azaz kifejezsekben ott fordulhatnak el, ahol egybknt aritmetikai (egsz) rtkek lehetnek. signed esetben a legmagasabb helyirtk bit (MSB - most significant bit) eljelbitknt viselkedik, azaz az int i : 2 lehetsges rtkei pldul:
00: 0, 01: +1, 10: -2, 11: -1

Az unsigned m : 4 lehetsges rtkei:


0000: 0, 0001: 1, . . ., 1111: 15

ltalnossgban:
unsigned x : szlessg; /* 0 <= x <= 2szlessg-1 */ signed y : szlessg; /* -2szlessg-1<= y <=+2szlessg-1-1 */

A nyelvben nincs sem egsz alul, sem tlcsorduls. Ha gy a bitmeznek brzolsi hatrain kvli rtket adunk, akkor abbl is lesz valami. Mghozz az rtk annyi als bitje, mint amilyen szles a bitmez. Pldul:
b.i = 6; /* 110 10, azaz -2 lesz az rtke! */

A bitmezk brzolsa gpfgg, mint mr mondottuk, azaz portbilis programokban kerljk el hasznlatukat! Vegyk el ismt a Bit szint opertorok fejezetben trgyalt dtum s idtrolsi problmt! Hogyan tudnnk ugyanazt a feladatot bitmezkkel megoldani?

250 Dtum: v 1980 hnap nap Bitpozci: 9 - 15 5-8 0-4

STRUKTRK S UNIK Id: ra perc kt msodperc Bitpozci: 11 15 5 10 04

A dtum s az id adatot egy-egy szban, azaz C nyelvi fogalmakkal egy-egy unsigned short int-ben tartjuk. A kt sz bitfelosztsa az brn lthat! A bitmezs megolds pldul a kvetkez is lehetne:
struct datum{ unsigned short nap: 5, ho: 4, ev: 7; } d = { 8, 3, 1996-1980 }; struct ido{ unsigned short mp2: 5, perc: 6, ora: 5; } i = { 2, 59, 11 }; /* . . . */ int ev=1996, ho=3, nap=8, ora=11, perc=59, mp=4; /* Rszeibl a dtum s az id ellltsa: */ d.ev = ev -1980; d.ho = ho; d.nap = nap; i.ora = ora; i.perc = perc; i.mp2 = mp >> 1; /* Ugyanez visszafel: */ ev = d.ev +1980; ho = d.ho; nap = d.nap; ora = i.ora; perc = i.perc; mp = i.mp2 << 1;

10.10 Balrtk jobbrtk Most mr tkletesen pontosthatjuk a balrtk kifejezst a Cben, mely: Egsz, lebegpontos, mutat, struktra vagy uni tpus azonost. Indexes kifejezs, mely nem tmbb (hanem elemm) rtkelhet ki. Tagszelektoros kifejezs (->, .). Nem tmbre hivatkoz, indirekcis kifejezs. Balrtk kifejezs zrjelben.

C programnyelv

251

A const objektum nem mdosthat balrtk, hisz csak a deklarciban kaphat kezdrtket. A jobbrtk (rvalue) olyan kirtkelhet kifejezs, melynek rtkt balrtk veheti fel. Pldul:
a = c + d; c + d = a; /* OK */ /* HIBS */

A balrtk (lvalue) olyan kifejezs, mely elri az objektumot (a hozz alloklt memria terletet). Trivilis pldul egy vltoz azonostja. Lehet azonban *P alak is, ahol a P kifejezst nem NULL mutatra rtkeli ki a fordt. Onnt is szrmaztathat a kt fogalom, hogy a balrtk llhat a hozzrendels opertor bal oldaln, s a jobbrtk pedig a jobb oldaln. Beszlhetnk mdosthat balrtkrl is! Mdosthat balrtk nem lehet tmb tpus (a tmbazonost praktikusan cm konstans), nem teljes tpus, vagy const tpusmdostval elltott objektum. Mdosthat balrtk pldul a konstans objektumra mutat mutat maga, mikzben a mutatott konstans objektum nem vltoztathat. Pldul
int tomb[20];

esetn balrtkek:
tomb[3] = 3; *(tomb+4) = 4;

A kvetkez deklarciban viszont a kar nem balrtk, hisz konstanssgra val tekintettel rtket egyedl a defincijban kaphat:
const char kar = k;

Azonban ha van egy


char *pozicio(int index);

fggvny, akkor balrtk lehet a kvetkez is:


*pozicio(5) = z;

10.11 Nvterletek A nvterlet az a hatskr, melyen bell egy azonostnak egyedinek kell lennie, azaz msms nvterleten konfliktus nlkl hasznlhat ugyanaz az azonost, s a fordt meg tudja klnbztetni ket. A nvterleteknek a kvetkez fajti vannak: Utasts cmke nvterlet: Az utasts cmkknek abban a fggvnyben kell egyedinek lennik, amelyben definiltk ket. Struktra, uni s enum cmke nvterlet: A struktra, az uni s az enum cmkk ugyanazon a nvterleten osztoznak. Deklarlsuk

252

STRUKTRK S UNIK blokkjban kell egyedinek bizonyulniuk. Ha minden fggvny testn kvl adjk meg ket, akkor viszont fjl hatskrben kell egyedinek lennik.

Struktra s unitagok (member) nvterlete: A tagneveknek abban a struktrban vagy uniban kell egyedinek lennik, amelyben deklarltk ket. Klnbz struktrkban s unikban elfordulhatnak ugyanazon tagazonostk akr ms tpussal, s eltolssal. sszestve: mindenegyes struktra s uni kln nvterlettel rendelkezik. Norml azonostk nvterlete: Idetartozik minden ms nv, ami nem frt be az elz hrom nvterletbe, azaz a vltoz, a fggvny (belertve a formlis paramtereket, s a loklis vltozkat) s az enumertorazonostk. Abban a hatskrben kell egyedinek bizonyulniuk, ahol definiljk ket. Pldul a fjl hatskr azonostknak ugyanebben a hatskrben kell egyedinek lennik. Tpusdefinci (typedef) nevek: Nem hasznlhatk azonostknt ugyanabban a hatskrben. Magyarn a tpusdefincis nevek a norml azonostk nvterletn vannak, de nem futsidej azonostk! Teht, ha a helyzetbl eldnthet, akkor lehet a tpusdefincis nv, s pldul egy loklis hatskr vltoz azonostja egyforma is:
typedef char FT; int fv(int lo){ int FT; /* Ez az FT egy int tpus loklis vltoz azonostja. */ /* . . . */ }

Nzznk nhny pldt!


struct s{ int s; /* OK: a struktratag jabb nvterleten helyezkedik el. */ float s;/*HIBS: gy mr kt azonos tagnv lenne egy struktrn bell. */ } s; /* OK: a norml vltozk nvterlete klnbzik minden eddig hasznlttl. */ union s{ /* HIBA: az s struktracmke is ezen a nvterleten van. */ int s; /* OK: hisz j tag nvterlet kezddtt. */ float f;/*OK: ms azonostj tag. */ } f; /* OK: hisz ez az f az norml vltozk nvterletn tallhat. */ struct t{ int s; /* OK: hiszen megint jabb tag nvterlet kezddtt. */

C programnyelv
/* . . . */ } s; /* HIBA: s azonost most mr minden nvterleten van. */ goto s; /* OK: az utasts cmke s a struktracmke ms-ms nvterleten vannak. */ /* . . . */ s: ; /* Utasts cmke. */

253

254

SZABVNY, MAGAS SZINT BEMENET, KIMENET

11 MAGAS SZINT BEMENET, KIMENET


A magas szint bemeneten s kimeneten olyan folyam, ram (stream) jelleg fjl, ill. eszkz (nyomtat, billentyzet, kperny stb.) kezelst rtnk, ami a felhasznl szempontjbl nzve szinte nincs tekintettel a mgttes hardverre, s gy a lehet legflexibilisebb kimenetet, bemenetet biztostja. A valsgban a folyamot egy FILE tpus struktrra mutat mutatval manipulljuk. Ezt a struktrt, a folyamkezel fggvnyek prototpusait stb. az STDIO.H fejfjlban definiltk. A struktra pldul legyen a kvetkez!
typedef struct{ short level; /* Puffer teltettsgi szint. */ unsigned short flags; /* Fjl llapotjelzk. */ char fd; /* Fjl ler. */ unsigned char hold; /* ungetc kar., ha nincs puffer. */ int bsize; /* A puffer mrete. */ unsigned char *buffer;/* A puffer cme. */ unsigned char *curp; /* Aktulis pozci a pufferben. */ /* . . . */ } FILE;

A programunkban
FILE *fp;

deklarcis utastssal FILE tpus struktrra mutat mutatt kell deklarlni, mely rtket a folyamot megnyit fopen, freopen fggvnyektl kap. Teht hasznlat eltt a folyamot meg kell nyitni. Megnyitsa a folyamot egy fjlhoz, vagy egy eszkzhz kapcsolja. Jelezni kell azt is ilyenkor, hogy a folyamot csak olvassra, vagy rsra, vagy mind kettre kvnjuk hasznlni stb. Ezutn elvgezhetjk a kvnt bemenetet, kimenetet a folyamon, majd legvgl le kell zrni. 11.1 Folyamok megnyitsa FILE *fopen(const char *fajlazonosito, const char *mod); A fggvny megnyitja a fajlazonositoval megnevezett fjlt, s folyamot kapcsol hozz. Visszaadja a fjlinformcit tartalmaz FILE struktrra mutat mutatt, mely a rkvetkez mveletekben azonostani fogja a folyamot, ill. NULL mutatt kapunk tle, ha a megnyitsi ksrlet sikertelen volt. A fajlazonosito termszetesen tartalmazhat (esetleg meghajt nevet) utat is, de a maximlis sszhossza FILENAME_MAX karakter lehet.

C programnyelv

255

A msodik paramter mod karakterlnc meghatrozza a ksbbi adattvitel irnyt, helyt s a folyam tpust. Nzzk a lehetsgeket! r w a r+ a+ Megnyits csak olvassra. Ltrehozs rsra. A mr ltez, ilyen azonostj fjl tartalma megsemmisl. Hozzfzs: megnyits rsra a fjl vgn, vagy ltrehozs rsra, ha a fjl eddig nem ltezett. Egy ltez fjl megnyitsa feljtsra (rsra s olvassra). Megnyits hozzfzsre: a fjl vgn feljtsra, vagy j fjl ltrehozsa feljtsra, ha a fjl eddig nem ltezett.

w+ j fjl ltrehozsa feljtsra. A ltez fjl tartalma elvsz.

A folyam tpusa szveges (text), vagy binris lehet. A szveges folyam a bemenetet s a kimenetet sorokbl llknak kpzeli el. A sorok vgt egy \n (LF) karakter jelzi. Lemezre trtn kimenet esetn a folyam a sorlezr \n karaktert \r\n karakter prral (CR-LF) helyettesti. Megfordtva: lemezes bemenetnl a CR-LF karakter prbl ismt LF karakter lesz. Ezt a manipulcit transzlcinak nevezzk. Bemenet esetn a folyam a 0X1A rtk karaktert fjlvgnek tekinti. sszegezve: a szveges folyam bizonyos, kitntetett karaktereket specilisan kezel, mg a binris folyam ilyent egyetlen karakterrel sem tesz. Elismerjk termszetesen, hogy nincs transzlci mindenegyes opercis rendszerben. A mod karakterlncban expliciten megadhatjuk a folyam tpust. A szvegest a t, a binrist a b jelli. A folyamtpus karakter a karakterlncban az els bet utn brhol elhelyezhet, azaz megengedettek az
rt+, r+t stb.

Nem ktelez azonban a folyamtpust a mod karakterlncban expliciten megadni. Ha elhagyjuk, alaprtelmezs a szveges. Ha a folyamot feljtsra (update) nyitottk meg, akkor megengedett mind a bemenet, mind a kimenet. A kimenetet azonban fflush, vagy pozcionl (fseek, rewind stb.) fggvny hvsa nlkl nem kvetheti kzvetlenl bemenet. A fordtott adatirnyvlts is csak fjlvgen, vagy e fggvnyek hvsnak kzbeiktatsval valsthat meg.

256

SZABVNY, MAGAS SZINT BEMENET, KIMENET

11.2 Folyamok pufferezse A fjlokhoz kapcsolt folyamok szoksosan pufferezettek, s a puffer lefoglalsa megnyitskor automatikusan megtrtnik malloc hvssal. Ez is megengedi azonban az egy karakteres szint bemenetet, kimenetet (getc, putc), ami nagyon gyors. A pufferrel kapcsolatos informcikat a FILE struktra tagjai rjk le: curp level buffer bsize , ahol buffer a puffer kezdcme s bsize a mrete. A curp a pufferbeli aktulis pozcira mutat, s level pedig szmllja, hogy mg hny karakter van htra a pufferben. A teljes pufferezettsg azt jelenti, hogy kirs automatikusan csak akkor trtnik, ha a puffer teljesen feltelt, ill. olvass csak akkor kvetkezik be, ha a puffer teljesen kirlt. Egy karakter rsa, vagy olvassa a curp pozcirl, ill. pozcira trtnik, s a mvelet mellkhatsaknt a curp eggyel n, s a level eggyel cskken. A pufferezetlensg azt jelenti, hogy a bjtok tvitele azonnal megtrtnik a fjlba (fjlbl), vagy az eszkzre (eszkzrl). A mai opercis rendszerek legtbbje a kisebb fjlokat megnyitsuk utn valamilyen rendszer terleten (cash) tartja, s a pufferek is csak a memriabeli fjllal vannak kapcsolatban. Clszer teht, a programfejleszt rendszer segtsgben utnanzni, hogy az azonnali fjlba rs, vagy olvass pontosan hogyan valsthat meg, ha igazn szksg van r. A setbuf s a setvbuf fggvnyhvsokkal kijellhetnk sajt puffert, mdosthatjuk a hasznlatos puffer mrett, vagy pufferezetlenn tehetjk a bemenetet s a kimenetet. void setbuf(FILE *stream, char *puff); A fggvny az automatikusan alloklt (malloc) puffer helyett a puff puffert hasznltatja a stream folyammal adattvitel esetn. Ha a puff paramter NULL mutat, akkor a folyam pufferezetlen lesz, msklnben a folyam teljesen pufferezett. A puffer klnben BUFSIZ mret. A szabvny bemenet (stdin) sorpufferezett s a szabvny kimenet (stdout) pufferezetlen, ha nincsenek az opercis rendszerben tirnytva, mert ekkor mindkett teljesen pufferezett. A sorpufferezettsg azt jelenti, hogy ha a puffer res, a kvetkez bemeneti mvelet megksrli a teljes

C programnyelv

257

puffer feltltst. Kimenet esetn mindig kirl a puffer, ha teljesen feltelik, ill. amikor \n karaktert runk bele. Elre megjsolhatatlan hiba kvetkezik be, ha a setbuf fggvnyt nem kzvetlenl a folyam megnyitsa utn hvjk meg. Leglis lehet mg a pufferezetlen folyamra vonatkoz setbuf hvs, brhol is kvetkezik be. Vigyzzunk a puffer auto trolsi osztly deklarcijval, mert akkor csak abbl a fggvnybl lesz elrhet, ahol deklarltuk! Mg szarvasabb a hiba, ha kilpnk a folyam lezrsa nlkl abbl a fggvnybl, melyre nzve a puffernk loklis volt. int setvbuf(FILE *stream, char *puff, int tipus, size_t meret); A fggvny ugyanazt teszi, mint a setbuf. Lthat azonban, hogy expliciten megadhat a puffer tipusa s merete. A size_t tpusbl kvetkezleg nagy mret puffer is elrhat. A pufferezetlensg ezzel a fggvnnyel a tipus paramter megfelel megadsval rhet el, ugyanis ha az aktulis puff paramtert NULL mutatnak vlasztjuk, akkor a rutin mallockal foglal memrit a puffernek. A tipus paramter lehetsges rtkei a kvetkezk: _IOFBF: A fjl teljesen pufferezett. Ha kirl, a kvetkez bemeneti mvelet megksrli teljesen feltlteni a puffert. Kimenet esetn fjlba rs automatikusan csak akkor trtnik, ha a puffer teljesen feltelt. _IOLBF: A fjl sorpufferezett. _IONBF: A fjl pufferezetlen. A puff s a meret paramter figyelmen kvl marad. Minden bemeneti s kimeneti mvelet kzvetlen adattvitelt jelent a fjlba. A setvbuf zrust ad vissza sikeres esetben, s nem zrust kapunk, ha a megadott tipus, vagy a meret paramter rvnytelen, vagy nincs elg memria a puffer alloklshoz. Nzznk egy pldt!
#include <stdio.h> char puff[BUFSIZ]; void main(void) { FILE *input, *output; if((input=fopen("file.in","r"))!=NULL){ if(output=fopen("file.out","w")){ if(setvbuf(input,puff,_IOFBF,BUFSIZ)) printf("Sikertelen a sajt input puffer alloklsa!\n");

258

SZABVNY, MAGAS SZINT BEMENET, KIMENET

/* A bemeneti folyam sajt puffert hasznlva, minimlis lemezhez fordulssal mveletre ksz. */ if(setvbuf(output,NULL,_IOLBF,128)) printf("Az output puffer alloklsa sikertelen!\n"); else{ /* A kimeneti folyam sorpufferezetten, malloc hvssal alloklt pufferrel mveletre ksz. */ /* Itt intzhet a fjlkimenet s bemenet! */ } /* Fjlok lezrsa. */ fclose(output); } else printf("Az output fjl megnyithatatlan!\n"); fclose(input); } else printf("Az input fjl megnyitsa sikertelen!\n"); }

Eddig csak a pufferek automatikus rtsrl beszltnk. Lehetsges azonban a pufferek kzi rtse is. St, adattviteli irnyvlts eltt a kimeneti puffert ki is kell rteni. Lssuk a fggvnyt! int fflush(FILE *stream); Kimenetre nyitott folyam esetn a rutin kirja a puffer tartalmt a kapcsolt fjlba. Bemeneti folyamnl a fggvny eredmnye nem definilhat, de tbbnyire trli a puffer tartalmt. Mindkt esetben nyitva marad a folyam. Pufferezetlen folyamnl e fggvny hvsnak nincs hatsa. Sikeres esetben zrust kapunk vissza. Hibs esetben a szolgltatott rtk EOF. Az fflush(NULL) rti az sszes kimeneti folyamot. 11.3 Pozcionls a folyamokban A folyamokat rendszerint szekvencilis fjlok olvassra, rsra hasznljk. A magas szint bemenet, kimenet a fjlt bjtfolyamnak tekinti, mely a fjl elejtl (0 pozci) indul s a fjl vgig tart. A fjl utols pozcija a fjlmret - 1. Az adattvitel mindig az aktulis fjlpozcitl kezddik, megtrtnte utn a fjlpozci a fjlban kvetkez, t nem vitt bjtra mozdul. A fjlpozcit fjlmutatnak is szoks nevezni. Eszkzhz kapcsolt folyam mindig csak szekvencilisan (zrustl indul, monoton nvekv fjlpozcival) rhet el. Lemezes fjlhoz kapcsolt folyam bjtjai azonban direkt (random) mdon is olvashatk s rhatk. Lemezes fjlok esetn a fjlmutat adattvitel eltti belltst az

C programnyelv int fseek(FILE *stream, long offset, int ahonnet);

259

fggvnnyel vgezhetjk el, mely a stream folyam fjlmutatjt offset bjttal az ahonnet paramterrel adott fjlpozcin tlra lltja be. Szveges folyamokra az offset zrus lehet, vagy egy az ftell fggvny ltal visszaadott rtk. Az ahonnet paramter a kvetkez rtkeket veheti fel: SEEK_SET: A fjl kezdettl. SEEK_CUR: Az aktulis fjlpozcitl. SEEK_END: A fjl vgtl. A fggvny elvet minden a bemenetre ungetcvel visszarakott karaktert. Az ungetcrl a kvetkez fejezetben lesz sz! Feljtsra megnyitott fjl esetn az fseek utn mind bemenet, mind kimenet kvetkezhet. A fggvny trli a fjlvg jelzt. Lsd a fjl llapotjelzi kzt mg ebben a fejezetben! A fggvny zrust ad vissza, ha a fjlpozcionls sikeres volt, ill. nem zrust kapunk hiba esetn. rjunk fjlmretet megllapt fggvnyt!
#include <stdio.h> long fajlmeret(FILE *stream) { long aktpoz, hossz; aktpoz=ftell(stream); fseek(stream, 0L, SEEK_END); hossz=ftell(stream); fseek(stream, aktpoz, SEEK_SET); return(hossz); }

A fajlmeret elteszi a pillanatnyi pozcit az aktpoz vltozba, hogy a fjl vgre llts utn helyre tudja hozni a fjlmutatt. A lekrdezett fjlvg pozci ppen a fjlmret. Nzzk a tovbbi fjlpozcival foglalkoz fggvnyeket! long int ftell(FILE *stream); A rutin visszaadja a stream folyam aktulis fjlpozcijt sikeres esetben, msklnben -1L-t kapunk tle. A void rewind(FILE *stream);

260

SZABVNY, MAGAS SZINT BEMENET, KIMENET

a stream folyam fjlmutatjt a fjl elejre lltja. Feljtsra megnyitott fjl esetn a rewind utn mind bemenet, mind kimenet kvetkezhet. A fggvny trli a fjlvg s a hibajelz biteket. A FILE struktra flags szava bitjei (llapotjelzi) a kvetkez jelentsek lehetnek!
#define #define #define #define #define #define #define #define /* . . . _F_RDWR _F_READ _F_WRIT _F_BUF _F_LBUF _F_ERR _F_EOF _F_BIN */ 0x0003 0x0001 0x0002 0x0004 0x0008 0x0010 0x0020 0x0040 /* /* /* /* /* /* /* /* olvass s rsjelz */ csak olvashat fjl */ csak rhat fjl */ malloc pufferelt */ sorpufferelt fjl */ hibajelz */ fjlvg jelz */ binris fjl jelz */

A megadott stream folyam aktulis fjlpozcijt helyezi el az int fgetpos(FILE *stream, fpos_t *pos); a pos paramterrel adott cmen. Ez a rtk felhasznlhat az fsetposban. A visszaadott rtk zrus hibtlan, s nem zrus sikertelen esetben. Az int fsetpos(FILE *stream, const fpos_t *pos); a stream folyam fjlmutatjt lltja be a pos paramterrel mutatott rtkre. Feljtsra megnyitott fjl esetn az fsetpos utn mind bemenet, mind kimenet kvetkezhet. A fggvny trli a fjlvg jelz bitet, s elvet minden, e fjlra vonatkoz ungetc karaktert. A visszakapott rtk egyezik az fgetposnl rottakkal. Vegyk szre, hogy az fseek s az ftell long rtkekkel dolgozik. A maximlis fjlmret gy 2GB lehet. Az fpos_t adattpus e korlt ttrst biztostja, hisz mgtte akr 64 bites egsz is lehet. 11.4 Bemeneti mveletek int fgetc(FILE *stream); A folyam kvetkez unsigned char karaktert adja vissza eljel kiterjeszts nlkl intt konvertltan, s eggyel elbbre lltja a fjlpozcit. Sikertelen esetben, ill. fjl vgn EOFot kapunk. A

C programnyelv int getc(FILE *stream);

261

makr, mint ahogyan ez a lehetsges defincijbl is ltszik, ugyanezt teszi:


#define getc(f) \ ((--((f)->level)>=0) ? (unsigned char)(*(f)->curp++) :\ _fgetc(f))

int ungetc(int c, FILE *stream); A fggvny visszateszi a stream bemeneti folyamba a c paramter unsigned char tpusv konvertlt rtkt gy, hogy a kvetkez olvasssal ez legyen az els elrhet karakter. A szablyos mkds csak egyetlen karakter visszahelyezse esetn garantlt, de a visszatett karakter nem lehet az EOF. Kt egymst kvet ungetc hvs hatsra mr csak a msodiknak visszatett karakter rhet el, mondjuk, a kvetkez getcvel, azaz az els elveszik. Gondoljuk csak meg, hogyha nincs puffer, akkor a visszattelhez a FILE struktra egyetlen hold tagja ll rendelkezsre! Az fflush, az fseek, az fsetpos, vagy a rewind trli a bemenetre visszatett karaktert. Sikeres hvskor az ungetc a visszatett karaktert adja vissza. Hiba esetn viszont EOFot kapunk tle. Az char *fgets(char *s, int n, FILE *stream); karakterlncot hoz be a stream folyambl, melyet az s cmtl kezdve helyez el a memriban. Az tvitel lell, ha a fggvny n - 1 karaktert, vagy \nt olvasott. A rutin a \n karaktert is kiteszi a lncba, s a vghez mg zr \0t is illeszt. Sikeres esetben az fgets az s karakterlncra mutat mutatval tr vissza. Fjlvgen, vagy hiba esetn viszont NULLt szolgltat. Vegyk szre, hogy a jegyzet eleje ta hasznlt getline fggvny csak annyiban tr el az fgetstl, hogy: A beolvasott karakterlnc mrett adja vissza. A szabvny bemenetrl (stdin) olvas, s nem ms folyambl, gy eggyel kevesebb a paramtere. n karaktert hoz be legfeljebb, vagy \nig, de magt a soremels karaktert nem teszi be az eredmny karakterlncba. size_t fread(void *ptr, size_t size, size_t n, FILE *stream);

262

SZABVNY, MAGAS SZINT BEMENET, KIMENET

A fggvny n * size bjtot olvas a stream folyambl, melyet a ptr paramterrel mutatott cmen helyez el. Visszaadott rtke nem a beolvasott bjtok szma, hanem a
beolvasott bjtok szma / size

sikeres esetben. Hiba, vagy fjlvg esetn ez persze nem egyezik nnel. Az eddig ismertetett bemeneti fggvnyek nem konvertltk a beolvasott karakter(lnco)t. Az int fscanf(FILE *stream, const char *format<, cim, ...>); viszont a stream folyambl karakterenknt olvasva egy sor bemeneti mezt vizsgl. Aztn minden mezt a format karakterlncnak megfelelen konvertl, s letrol rendre a paramter cimeken. A format karakterlncban ugyanannyi konverzit okoz formtumspecifikcinak kell lennie, mint ahny bemeneti mez van. A jellsben a <> az elhagyhatsgot, a ... a megelz paramter tetszleges szm ismtelhetsgt jelenti. A bemeneti mez defincija, a formzs s a konvertls rszletei a scanf fggvny lersban tallhatk meg! Az fscanf a sikeresen vizsglt, konvertlt s letrolt bemeneti mezk szmval tr vissza. Ha a fggvny az olvasst a fjl vgn ksreln meg, vagy valamilyen hiba trtnne, akkor EOF-ot kapunk tle vissza. A rutin zrussal is visszatrhet, ami azt jelenti, hogy egyetlen vizsglt mezt sem trolt le. 11.5 Kimeneti mveletek int fputc(int c, FILE *stream); A fggvny a c unsigned char tpusv konvertlt rtkt rja ki a stream folyamba. Sikeres esetben a c karaktert kapjuk vissza tle, hiba bekvetkeztekor viszont EOFot. A int putc(int c, FILE *stream); makr, mint ahogyan ez a lehetsges defincijbl is ltszik, ugyanezt teszi:
#define putc(c,f) \ ((++((f)->level)<0) ? (unsigned char)(*(f)->curp++)=(c)) :\ _fputc((c),f))

int fputs(const char *s, FILE *stream);

C programnyelv

263

A fggvny az s karakterlncot kirja a stream folyamba. Nem fz hozz \n karaktert, s a lezr \0 karakter sem kerl t. Sikeres esetben nem negatv rtkkel tr vissza. Hiba esetn viszont EOFot kapunk tle. Az size_t fwrite(const void *ptr, size_t size, size_t n, FILE *stream); a ptr cmmel mutatott memria terletrl n * size bjtot r ki a stream folyamba. Visszaadott rtke nem a kirt bjtok szma, hanem a
kirt bjtok szma / size

sikeres esetben. Hiba bekvetkeztekor ez nem egyezik nnel. Az eddigi kimeneti fggvnyek nem vgeztek konverzit. Az int fprintf(FILE *stream, const char *format<, parameter, ...>); fogad egy sor parametert, melyeket a format karakterlncnak megfelelen formz (konvertl), s kivisz a stream folyamba. A format karakterlncban ugyanannyi konverzit okoz formtumspecifikcinak kell lennie, mint ahny parameter van. A jellsben a <> az elhagyhatsgot, a ... a megelz paramter tetszleges szm ismtelhetsgt jelenti. A formzs s a konvertls rszletei a printf fggvny lersban tallhatk meg! Az fprintf a folyamba kivitt karakterek szmval tr vissza sikeres esetben, ill. EOF-ot kapunk tle hiba bekvetkeztekor. 11.6 Folyamok lezrsa int fclose(FILE *stream); A rutin lezrja a stream folyamot. Ez eltt azonban rti a folyamhoz tartoz puffert, s a pufferhez automatikusan alloklt memrit fel is szabadtja. Ez utbbi nem vonatkozik a setbuf, vagy a setvbuf fggvnyekkel hozzrendelt pufferekre. Ezek gyei csak a felhasznlra tartoznak. Sikeres esetben az fclose zrussal tr vissza. Hiba esetn viszont EOF ot kapunk tle. 11.7 Hibakezels Tudjuk, hogy a szabvny knyvtri fggvnyek gy a magas szint bemenetet, kimenetet kezelk is a hibt, a kivteles esetet gy jelzik, hogy valamilyen specilis rtket (EOF, NULL mutat, HUGE_VAL stb.) adnak vissza, s az errno globlis hibavltozba belltjk a hiba

264

SZABVNY, MAGAS SZINT BEMENET, KIMENET

kdjt. A hibakdok az ERRNO.H fejfjlban definilt, egsz, nem zrusrtk szimbolikus llandk. Programunk indulsakor a szabvny bemeneten (stdin) s kimeneten (stdout) tl a hibakimenet (stderr) is rendelkezsre ll, s a hibazeneteket ez utbbin clszer megjelentetni. Az stderr a kperny (karakteres ablak) alaprtelmezs szerint, s nem is irnythat t fjlba a legtbb opercis rendszerben, mint ahogyan a bemenettel (<) s a kimenettel (>) ez megtehet volt a programot futtat parancssorban. Mit jelentessnk meg hibazenetknt az stderren? Termszetesen brmilyen szveget kirathatunk, de a hibakdokhoz programfejlszt rendszertl fggen hibazenet karakterlncok is tartoznak, s ezek is megjelentethetk. A hibakodhoz tartoz hibazenet karakterlnc kezdcmt szolgltatja a szabvnyos #include <STRING.H> char *strerror(int hibakod); fggvny, s az zenet meg is jelentethet
fprintf(stderr, Hiba: %s\n, strerror(hibakod));

mdon. A void perror(const char *s); kirja az stderrre azt a hibazenetet, melyet a legutbbi hibt okoz, knyvtri fggvny hvsa idzett el. Elszr megjelenteti az s karakterlncot a rutin, aztn kettspontot (:) tesz, majd az errno aktulis rtknek megfelel zenet karakterlncot rja ki lezr \n-nel. Teht pldul a perror(Hiba: ) megfelel a
fprintf(stderr, Hiba: %s\n, strerror(errno));

fggvnyhvsnak. Vigyzat! Az errno rtkt csak kzvetlenl a hibt okoz rutin hvsa utn szabad felhasznlni, mert a kvetkez knyvtri fggvny megidzse fellrhatja e globlis vltoz rtkt. Ha a hibakddal mgis ksbb kvnnnk foglalkozni, akkor tegyk el az errno rtkt egy segdvltozba! Folyamokkal kapcsolatban a perror s paramtere a fjlazonost szokott lenni. Meg kell trgyalnunk mg hrom, csak a folyamok hibakezelsvel foglalkoz fggvnyt! A

C programnyelv void clearerr(FILE *stream);

265

nullzza a stream folyam fjlvg s hibajelzjt. Ha a folyam hibajelz bitje egyszer bebillent, akkor minden a folyamon vgzett mvelet hibval tr vissza mindaddig, mg a hibajelzt e fggvnnyel, vagy a rewinddal nem trlik. A fjlvg jelz bitet egybknt minden bemeneti mvelet nullzza. Az int feof(FILE *stream); tbbnyire makr, mely a kvetkez lehetsges
#define feof(f) ((f)->flags & _F_EOF)

defincija miatt, visszaadja a fjlvg jelz bit llapott, azaz vlaszt ad a fjlvg van-e krdsre. Az egyszer bebillent fjlvg jelz bit a kvetkez, e folyamra vonatkoz bemeneti, pozcionl mveletig, vagy clearerrig 1 marad. Az int ferror(FILE *stream); makr ebben a szellemben
#define ferror(f) ((f)->flags & _F_ERR)

a hiba jelz bit llapott adja vissza. Az egyszer bebillent hiba jelz bitet csak a clearerr s a rewind fggvnyek trlik. Ha a krdses folyammal kapcsolatban a bebillent hiba jelz bit trlsrl nem gondoskodunk, akkor minden e folyamra meghvott tovbbi fggvny hibt jelezve fog visszatrni. rjuk meg az fputc segtsgvel az fputs fggvnyt!
int fputs(const char *s, FILE *stream){ int c; while(c=*s++) if(c!=fputc(c, stream)) break; return ferror(stream) ? EOF : 1; }

Ksztsnk szoftvert komplett hibakezelssel, mely az els parancssori paramtere fjlt tmsolja a msodik paramtere azonostj fjlba! Ha a programot nem elg parancssori paramterrel indtjk, akkor ismertesse hasznlatt! A msolsi folyamat elrehaladsrl tjkoztasson felttlenl!
/* PELDA31.C: Els paramter fjl msolsa a msodikba. */ #include <stdio.h> #include <string.h>/* strerror miatt! */

266

SZABVNY, MAGAS SZINT BEMENET, KIMENET

#include <errno.h> /* Az errno vgett! */ #define KENT 10 /* Hnyanknt jelenjen meg a szmll.*/ #define SZELES 10 /* Mezszlessg a szml kzlshez. */ int main(int argc, char *argv[]){ FILE *be, *ki; /* A be s kimeneti fjlok. */ long szlo=0l; /* A szmll. */ int c; /* A kv. karakter s segdvltoz. */ printf("Az els paramter fjl msolsa a msodikba:\n"); if(argc<3){ fprintf(stderr, "Programindts:\n" "PELDA30 forrsfjl clfjl\n"); return 1; } if(!(be=fopen(argv[1],"rb"))){ perror(argv[1]); return 1; } if(!(ki=fopen(argv[2],"wb"))){ perror(argv[2]); fclose(be); return 1; } printf("%s --> %s:\n%*ld",argv[1],argv[2], SZELES, szlo);

A formtumspecifikcibeli * SZELES mezszlessget eredmnyez.


while((c=fgetc(be))!=EOF){/* Olvass fjlvgig, vagy hibig. */ if(c==fputc(c,ki)){ /* Kirs rendben. */ if(!(++szlo%KENT)){ for(c=0; c<SZELES; ++c) fputc('\b', stdout); printf("%*ld", SZELES, szlo); } } else{ /* Kirsnl hiba van. */ perror(argv[2]); clearerr(ki); if(!fclose(ki)) /* A flksz fjl trlse. */ remove(argv[2]); else perror(argv[2]); fclose(be); return 1; } } /* Az olvass EOF rtkkel fejezdtt be. */ c=errno; /* Hibakd mentse. */ fclose(ki); /* A vgs mret kirsa: */ for(c=0; c<SZELES; ++c) fputc('\b', stdout); printf("%*ld\n", SZELES, szlo); if(ferror(be)){ /* Hiba volt. */ fprintf(stderr, "%s: %s\n", argv[1], strerror(c)); clearerr(be); fclose(be); remove(argv[2]); return 1; } fclose(be); /* Minden rendben ment. */

C programnyelv
return 0; }

267

Az stdoutra irnyul mveletek hibakezelsvel azrt nem foglalkoztunk, mert ahol az sem mkdik, ott az opercis rendszer sem megy. Megoldand feladatok: Fokozzuk kicsit a PELDA31.Cben megvalstott feladatot! A fjlba rs norml esetben akkor nem megy, ha betelik a lemez. Ezen prbljunk meg gy segteni, hogy a forrsfjl megnyitsa utn llaptsuk meg a mrett! A clfjlnak is foglaljunk helyet (fseek) ugyanekkora mretben, majd feljtva rjuk ki r a forrs tartalmt! Ksztsen szoftvert, mely eldnti az indt parancssorban megadott azonostj fjl tpust, azaz hogy szveges, vagy binris! Ha parancssori paramter nlkl futtatjk a programot, akkor ismertesse a hasznlatt! rjon szoftvert, mely az indt parancssorban megadott szvegfjlokat egyesti a megads sorrendjben a parancssorban utolsknt elrt azonostj szvegfjlba! Ha parancssori paramter nlkl indtjk a programot, akkor ismertesse a kpernyn, hogyan kell hasznlni! Ha csak egy fjlazonost van a parancssorban, akkor a szabvny bemenet msoland bele. A fjlok egyestse sorn a folyamat elrehaladsrl tjkoztatni kell a kpernyn! A szabvny bemenet msolsa esetn vgl kzlend mg az eredmnyfjl mrete! 11.8 Elre definilt folyamok Egy idben legfeljebb FOPEN_MAX, vagy OPEN_MAX folyam (fjl) lehet megnyitva. Ennek megfelel a globlis FILE struktratmb
extern FILE _streams[];

mrete is, melybl radsul mg az els hrom bizonyosan foglalt is:


#define #define #define stdin (&_streams[0]) stdout (&_streams[1]) stderr (&_streams[2])

A globlis FILE struktratmb neve persze lehet ettl eltr is. Ezek az elre definilt folyamok, melyek programunk futsnak megkezdsekor mr megnyitva rendelkezsre llnak.

268 Nv stdin stdout stderr

SZABVNY, MAGAS SZINT BEMENET, KIMENET B/K bemenet kimenet kimenet Tpus szveges szveges szveges Folyam szabvnyos bemenet szabvnyos kimenet szabvny hibakimenet Alaprtelmezs CON: CON: CON:

Az stdin s az stdout tirnythat a programot indt parancssorban szvegfjlba.


program < bemenet.txt > kimenet.txt

Ha nincsenek tirnytva, akkor az stdin sorpufferezett, s az stdout pedig pufferezetlen. Ilyen az stderr is, teht pufferezetlen. A legtbb opercis rendszerben cs (pipe) is hasznlhat. Pldul:
program1 | program2 | program3

program1 a rendszerben belltott szabvny bemenettel rendelkezik. Szabvny kimenete szabvny bemenete lesz program2nek, aminek szabvny kimenete program3 szabvny bemenete. Vgl program3 szabvny kimenete az, amit a rendszerben belltottak. Mindhrom elre definilt folyam tirnythat a programban is, azaz ha nem felelne meg az alaprtelmezs szerint a folyamhoz kapcsolt eszkz, akkor ezt kicserlhetjk az FILE *freopen(const char *fajlazonosito, const char *mod, FILE *stream); fggvnnyel a fajlazonositoj fjlra. A rutin els kt paramternek rtelmezse s visszaadott rtke egyezik az fopenvel. A harmadik viszont az elre definilt folyam: stdin, stdout vagy stderr. Az freopen persze nem csak elre definilt folyamokra hasznlhat, hanem brmilyen mssal is, de ez a legjellemzbb alkalmazsa. Ksztsnk programot, mely a szabvny bemenetrl rkez karaktereket a parancssori paramterknt megadott szvegfjlba msolja! Ha indtskor nem adnak meg parancssori paramtert, akkor csak echzza a szoftver a bementet a kimeneten! A feladatot az stdout tirnytsval oldjuk meg.
/* PELDA32.C: Bemenet msolsa fjlba stdout-knt. */ #include <stdio.h> #include <stdlib.h> /* A system rutin miatt! */ #define PUFF 257 /* A bemeneti puffer mrete. */

C programnyelv
int main(int argc, char *argv[]){ char puff[PUFF]; /* Bemeneti puffer. */ if(system(NULL)) system("CLS"); printf("A szabvny bemenet fjlba msolsa " "Ctrl+Z-ig:\n"); if(argc<2) printf("A program indthat gy is:\n" "PELDA32 szvegfjl\n\n"); else if(!freopen(argv[1],"wt", stdout)){ perror(argv[1]); return 1; } while(fgets(puff, PUFF, stdin)){ if(fputs(puff, stdout)<0){ perror(argv[1]); clearerr(stdout); if(!fclose(stdout)) remove(argv[1]); else perror(argv[1]); return 1; } } return 0; }

269

Az STDLIB.H bekapcsolsval rendelkezsre ll int system(const char *parancs); rutin parancs paramtert tadja vgrehajtsra az opercis rendszernek (a parancsrtelmeznek), azaz vgrehajtatja a rendszerrel a parancsot. A fggvny visszatrsi rtke a programfejleszt rendszertl fgg, de tbbnyire a parancsrtelmez ltal szolgltatott rtk az. Ha a parancs NULL, akkor a rutin a parancsrtelmez ltezsrl szmol be, azaz ha van, nem zrussal tr vissza, s zrust szolgltat, ha nincs. 11.8.1 Bemenet az stdin-rl int getchar(void); A fggvny makr, azaz:
#define getchar() getc(stdin)

A char *gets(char *s); az fgetshez hasonlan karaktereket olvas az stdinrl, melyeket rendre elhelyez a paramter s karaktertmbben. A visszaadott rtke is egyezik az fgetsvel, azaz norml esetben st szolgltatja, s fjlvg vagy hiba bekvetkeztekor NULLt. Az stdinrl val olvass azonban az els \n karakterig tart. Magt az LF karaktert nem viszi t az s tmbbe, hanem helyette a karakterlncot zr \0t r oda. A konverzit is vgz

270

SZABVNY, MAGAS SZINT BEMENET, KIMENET

int scanf(const char *format<, cim, ...>); fggvny az fscanfhoz hasonlan de az stdin folyambl olvasva egy sor bemeneti mezt vizsgl. Aztn minden mezt a format karakterlncnak megfelelen formz (konvertl), s letrol rendre a paramter cmeken. A jellsben a <> az elhagyhatsgot, a ... a megelz paramter tetszleges szm ismtelhetsgt jelenti. A bemeneti mez defincijra rgtn kitrnk! A scanf a sikeresen vizsglt, konvertlt s letrolt bemeneti mezk szmval tr vissza. A vizsglt vagy akr konvertlt, de le nem trolt mezk ebbe a szmba nem rtendk bele. Ha a fggvny az olvasst a fjl vgn ksreln meg, vagy valamilyen hiba kvetkezne be, akkor EOFot kapunk tle vissza. A fggvny zrussal is visszatrhet, ami azt jelenti, hogy egyetlen vizsglt mezt sem trolt le. A format karakterlncban ugyanannyi formtumspecifikcinak kell lennie, mint ahny bemeneti mez van, s ahny cim paramtert megadtak a hvsban. Ha a formtumspecifikcik tbben vannak, mint a cimek, akkor ez elre megjsolhatatlan hibhoz vezet. Ha a cim paramterek szma tbb mint a formtumspecifikcik, akkor a felesleges cimeket egyszeren elhagyja a scanf. A format karakterlnc hrom fle objektumbl ll: fehr karakterekbl, nem fehr karakterekbl s formtumspecifikcikbl. Ha fehr karakter kvetkezik a format karakterlncban, akkor a scanf olvassa, de nem trolja a bemenetrl rkez fehr karaktereket egszen a kvetkez nem fehr karakterig. Nem fehr karakter minden ms a % kivtelvel. Ha a format karakterlncban ilyen karakter kvetkezik, akkor a scanf olvas a bemenetrl, de nem trol, hanem elvrja, hogy a beolvasott karakter egyezzen a format karakterlncban levvel. A formtumspecifikcik vezrlik a scanf fggvnyt az olvassban, a bemeneti mezk konverzijban s a konverzi tpusban. A konvertlt rtket aztn a rutin elhelyezi a soron kvetkez paramterrel adott cim en. A formtumspecifikci ltalnos alakja:
% <*> <szlessg> <h|l|L> tpuskarakter

C programnyelv

271

, ahol a <> az elhagyhatsgot s a | a vagylagossgot jelli. Nzzk a rszleteket! Minden formtumspecifikci % karakterrel indul, s tpuskarakterrel vgzdik. Az ltalnos alakban elhagyhatnak jellt rszek csak az ott megadott sorrendben kerlhetnek a % s a tpuskarakter kz. A * elnyomja a kvetkez bemeneti mez hozzrendelst. A scanf a %*tpuskarakter hatsra olvassa, ellenrzi s konvertlja a vonatkoz bemeneti mezt, de nem helyezi el a kapott rtket az idetartoz cim paramteren. Teht a bemeneti mez tartalmnak ilyenkor is meg kell felelnie a konverzis tpuskarakternek. A szlessg maximlis mezszlessget hatroz meg, azaz a scanf legfeljebb ennyi karaktert olvashat, de olvashat ennl kevesebbet is, ha fehr, vagy konvertlhatatlan karakter kvetkezik a bemeneten. A h, az l s az L a cim paramter alaprtelmezs szerinti tpust mdostja. A h short int. Az l long int, ha a tpuskarakter egsz konverzit specifikl, ill. double, ha a tpuskarakter lebegpontos talaktst r el. Az L pedig a long double mdostja. A kvetkez tblzatban felsoroljuk az aritmetikai konverzit okoz tpuskaraktereket: Tpuskarakter d i o u x e, E f g, G Az elvrt bemenet decimlis egsz decimlis, oktlis vagy hexadecimlis egsz oktlis egsz (vezet 0 nlkl is annak minsl a szm) eljel nlkli decimlis egsz hexadecimlis egsz (vezet 0x vagy 0X nlkl is az a szm) lebegpontos vals lebegpontos vals lebegpontos vals A paramter tpusa int * int * int * unsigned int * int * float * float * float *

A %d, a %i, a %o, a %x, a %D, a %I, a%O, a %X, a %c s a %n konverzik esetn unsigned charra, unsigned intre, vagy unsig-

272

SZABVNY, MAGAS SZINT BEMENET, KIMENET ned longra mutat mutatk is hasznlhatk azoknl az talaktsoknl, ahol a charra, az intre, vagy a longra mutat mutat megengedett.

A %e, a %E, a %f, a %g s a %G lebegpontos konverzik esetn a bemeneti mezben lev vals szmnak ki kell elgtenie a kvetkez formt:
<+|-> ddddddddd <.> dddd <E|e> <+|-> ddd

ahol d decimlis, oktlis, vagy hexadecimlis szmjegyet, a <> elhagyhatsgot s a | vagylagossgot jell. A mutat konverzi tpuskarakterei: Tpuskarakter Az elvrt bemenet A paramter tpusa n Nincs. int *. A %n-ig sikeresen olvasott karakterek szmt trolja ebben az int-ben a scanf.

Megvalststl void * fgg formban, de ltalban hexadecimlisan.

A karakteres konverzi tpuskarakterei: Tpuskarakter Az elvrt bemenet A paramter tpusa c karakter Mutat charra, ill. mutat char tmbre, ha mezszlessget is megadtak. Pl.: %7c. Nincs konverzi. Magt a % karaktert trolja. Mutat char tmbre. Mutat char tmbre. Mutat char tmbre.

% s [kereskszlet]

% karakter karakterlnc karakterlnc

[^kereskszlet] karakterlnc

A %c hatsra a scanf a kvetkez karaktert (akr fehr, akr nem) olvassa a bemenetrl. Ha a fehr karaktereket t kvnjuk lpni, hasznljuk a %1s formtumspecifikcit!

C programnyelv

273

A %szlessgc specifikcihoz tartoz cim paramternek legalbb szlessg elem karaktertmbre kell mutatnia. A %s specifikcihoz tartoz cim paramternek legalbb akkora karaktertmbre kell mutatnia, melyben a vonatkoz bemeneti mez minden karaktere, s a karakterlncot lezr \0 is elfr. A %[kereskszlet] s a %[^kereskszlet] alak specifikci teljes mrtkben helyettesti az s tpuskaraktert. A vonatkoz cim paramternek karaktertmbre kell ekkor is mutatnia. A szgletes zrjelben lev karaktereket kereskszletnek nevezzk. %[kereskszlet] esetben a scanf addig olvassa a bemenetet, mg a bejv karakterek egyeznek a kereskszlet valamelyik karaktervel. A karaktereket kiteszi rendre a rutin \0val lezrtan a paramter karaktertmbbe. Pldul a %[abc]-vel az a, a b s a c karakterek valamelyikt kerestetjk a bemeneti mezben. A %[]xyz] viszont a ], az x, a y s a z utn kutat. %[^kereskszlet] a scanf brmilyen olyan karaktert keres, ami nincs benn a kereskszletben. Pldul a %[^]abc] hatsra addig tart a bemenet olvassa, mg rla ], a, b vagy c nem rkezik. Nhny programfejleszt rendszer esetn a kereskszletben tartomny is megadhat, azaz pldul a %[0123456789]-et a %[0-9] teljes mrtkben helyettesti. A tartomny kezd karaktere kdjnak azonban kisebbnek kell lenni a tartomny vg karaktere kdjnl. Nzznk nhny pldt! %[-+*/]: A ngy aritmetikai opertort keresi. %[0-9A-Za-z]: Alfanumerikus karaktert keres. %[+0-9-A-Z]: A +, a -, a szm s a nagybet karaktereket keresi. %[z-a]: A z, a - s az a karaktereket keresi. Tisztzzuk vgre a bemeneti mez fogalmt! Minden karakter a kvetkez fehr karakterig, de a fehr karakter maga mr nem tartozik bele. Minden karakter az els olyan karakterig, mely az aktulis tpuskarakter szerint nem konvertlhat. Minden karakter, mg a megadott mezszlessg ki nem merl.

274

SZABVNY, MAGAS SZINT BEMENET, KIMENET

Kereskszlet esetn addig tart a bemeneti mez, mg a kereskszlet feltteleinek meg nem felel karakter nem rkezik a bemenetrl. A bemeneti mez msodik alternatvja miatt, nem javasoljuk a scanf fggvny szleskr hasznlatt programokban. Helyette olvassuk be a bemeneti karakterlncot, vgezzk el rajta az sszes formai ellenrzst! Ha aztn minden rendben volt, a konverzi megvalsthat egy menetben az int sscanf(const char *puffer, const char *format<, cim, ...>); fggvnnyel, mely ugyanazt teszi, mint a scanf, de bemeneti mezit nem az stdinrl, hanem az els paramterknt kapott karakterlncbl veszi. 11.8.2 Kimenet az stdout-ra int putchar(int c); A fggvny makr, azaz:
#define putchar(c) putc((c), stdout)

A int puts(const char *s); fggvny a \0 lezrs s karakterlncot az stdout folyamba rja a \0 nlkl, mely helyett viszont kitesz mg egy \n karaktert. Sikeres esetben nem negatv rtkkel tr vissza. Hiba bekvetkeztekor viszont EOF-ot kapunk tle. A konverzit is vgz int printf(const char *format<, parameter, ...>); rutin fogad egy sor parametert, melyek mindegyikhez hozzrendel egy, a format karakterlncban lv formtumspecifikcit, s az ezek szerint formzott (konvertlt) adatokat kiviszi az stdout folyamba. A jellsben a <> az elhagyhatsgot, a ... a megelz paramter tetszleges szm ismtelhetsgt jelenti. A format karakterlncban ugyanannyi formtumspecifikcinak kell lennie, mint ahny parameter van. Ha kevesebb a paramter, mint a formtumspecifikci, akkor ez elre megjsolhatatlan hibhoz vezet. Ha tbb a paramter, mint a formtumspecifikci, akkor a felesleges paramtereket egyszeren elhagyja a printf.

C programnyelv

275

A rutin a folyamba kivitt bjtok szmval tr vissza sikeres esetben, ill. EOFot kapunk tle hiba bekvetkeztekor. A format karakterlnc ktfle objektumot tartalmaz: sima karaktereket s formtumspecifikcikat. A sima karaktereket vltozatlanul kiviszi az stdout-ra a printf. A formtumspecifikcihoz veszi a kvetkez parameter rtkt, konvertlja, s csak ezutn teszi ki az stdout-ra. A formtumspecifikci ltalnos alakja a kvetkez:
% <jelzk> <szlessg> <.pontossg> <h|l|L> tpuskarakter

Minden formtumspecifikci % karakterrel kezddik, s tpuskarakterrel vgzdik. Ha a % karaktert szeretnnk az stdoutra vinni, akkor meg kell duplzni (%%). Az ltalnos alakban elhagyhatnak jellt rszek csak az ott megadott sorrendben kerlhetnek a % s a tpuskarakter kz. A kvetkezkben lerjuk a tpuskarakterek rtelmezst arra az esetre, ha a formtumspecifikciban a % jelet csak a tpuskarakter kveti. Nzzk elbb az aritmetikai konverzit okoz tpuskaraktereket:

276

SZABVNY, MAGAS SZINT BEMENET, KIMENET Elvrt paramter int int int int int int double double double double double A kimenet formja Eljeles decimlis egsz. Eljeles decimlis egsz. Eljel nlkli oktlis egsz vezet 0 nlkl. Eljel nlkli decimlis egsz. Eljel nlkli hexadecimlis egsz (a, b, c, d, e, f-fel), de vezet 0x nlkl. Eljel nlkli hexadecimlis egsz (A, B, C, D, E, F-fel), de vezet 0X nlkl. <->dddd.dddd alak eljeles rtk. <->d.ddd...e<+|->ddd alak eljeles rtk. <->d.ddd...E<+|->ddd alak eljeles rtk. Az adott rtktl s a pontossgtl fggen e, vagy f alakban eljeles rtk. Ugyanaz, mint a g forma, de az e alak hasznlata esetn az exponens rszben E van.

Tpuskarakter d i o u x X f e E g G

e vagy E tpuskarakter esetn a vonatkoz paramter rtkt a printf


<->d.ddd...e<+|->ddd

alakra konvertlja, ahol: Egy decimlis szmjegy (d) mindig megelzi a tizedes pontot. A tizedes pont utni szmjegyek szmt a pontossg hatrozza meg. A kitev rsz mindig legalbb kt szmjegyet tartalmaz. f tpuskarakternl a vonatkoz paramter rtkt a printf
<->ddd.ddd...

alakra konvertlja, s a tizedes pont utn kirt szmjegyek szmt itt is a pontossg hatrozza meg. g vagy G tpuskarakter esetn a printf a vonatkoz paramter rtkt e, E, vagy f alakra konvertlja Olyan pontossggal, melyet a szignifikns szmjegyek szma meghatroz.

C programnyelv

277

A kvet zrusokat levgja az eredmnyrl, s a tizedes pont is csak akkor jelenik meg, ha szksges, azaz van mg utna rtkes trt szmjegy. A g e, vagy f formj, a G pedig E, vagy f alak konverzit okoz. Az e, ill. az E formt akkor hasznlja a printf, ha a konverzi eredmnyben a kitev nagyobb a pontossgnl, vagy kisebb 4nl. A karakteres konverzi tpuskarakterei: Tpuskarakter % c s Elvrt paramter nincs int char * A kimenet formja Nincs konverzi. Maga a % karakter jelenik meg. Egyetlen karakter. A karakterlnc karakterei megjelennek a zr \0t kivve. Ha megadtak pontossgot, akkor legfeljebb annyi karaktert r ki a printf.

278

SZABVNY, MAGAS SZINT BEMENET, KIMENET

A mutat konverzi tpuskarakterei: Tpuskarakter n Elvrt paramter int * A kimenet formja A paramter ltal mutatott int-ben letrolja az eddig kirt karakterek szmt. Nincs klnben semmilyen konverzi. A paramtert mutatknt jelenteti meg. A kijelzs formtuma programfejleszt rendszer fgg, de ltalban hexadecimlis.

void *

Lssuk a jelzket! Az eredmny balra igaztott, s jobbrl szkzzel prnzott. Ha a - jelzt nem adjk meg, akkor az eredmny jobbra igaztott, s balrl szkzkkel, vagy zrusokkal prnzott. Eljeles konverzi eredmnye mindig plusz, vagy mnusz eljellel kezddik. Ha a + jelzvel egytt szkz jelzt is megadnak, akkor a + jelz van rvnyben. Ha az rtk nem negatv, a kimenet egy szkzzel kezddik a plusz eljel helyett. A negatv rtk ilyenkor is mnusz eljelet kap. Azt hatrozza meg, hogy a paramtert alternatv formt hasznlva kell konvertlni. A # hatsa a paramterre Nincs hats. Az eredmnyben mindenkppen lesz tizedes pont mg akkor is, ha azt egyetlen szmjegy sem kveti. Normlisan ilyenkor nem jelenik meg a tizedes pont. Ugyanaz, mint e s E, de az eredmnybl a kvet zrusokat nem vgja le a printf. 0-t r a konvertlt, nem zrus paramter rtk el. Ez az oktlis szm megjelentetse. 0x, 0X elzi meg a konvertlt, nem zrus paramter rtket.

szkz

Az alternatv formk a tpuskaraktertl fggnek: Tp.kar. c,s,d,i,u e,E,f

g,G o x, X

C programnyelv

279

A szlessg a kimeneti rtk minimlis mezszlessgt hatrozza meg, azaz a megjelen eredmny legalbb ilyen szlessg. A szlessget kt mdon adhatjuk meg: vagy expliciten berjuk a formtumspecifikciba, vagy a szlessg helyre * karaktert tesznk. Ilyenkor a printf hvsban a kvetkez parameter csak int tpus lehet, s ennek az rtke definilja a kimeneti rtk mezszlessgt. Brmilyen szlessget is runk el, a printf a konverzi eredmnyt nem csonktja! A lehetsges szlessg specifikcik: Szlessg n Hatsa a kimenetre A printf legalbb n karaktert jelentet meg. Ha a kimeneti rtk n karakternl kevesebb, akkor szkzzel n karakteresre prnzza (jobbrl, ha a jelzt megadtk, msklnben balrl). Legalbb n karakter jelenik meg ekkor is. Ha a kimeneti rtk nnl kevesebb karakterbl ll, akkor balrl zrus feltlts kvetkezik. A paramter lista szolgltatja a szlessg specifikcit, de ennek a paramter listban meg kell elznie azt a paramtert, amire az egsz formtumspecifikci vonatkozik.

0n

A pontossg specifikci mindig ponttal (.) kezddik. A szlessghez hasonlan ez is megadhat kzvetlenl, vagy kzvetve (*) a paramter listban. Utbbi esetben egy int tpus paramternek meg kell elznie azt a paramtert a printf hvsban, amire az egsz formtumspecifikci vonatkozik. Megemltjk, hogy a szlessget s a pontossgot is megadhatjuk egyszerre kzvetetten. Ilyenkor a formtumspecifikciban *.* van. A printf hvs paramter listjban kt int tpus paramter elzi meg (az els a szlessg, a msodik a pontossg) azt a paramtert, amire az egsz formtumspecifikci vonatkozik. Lssunk egy pldt!
printf("%*.*f", 6, 2, 6.2);

A 6.2et f tpuskarakterrel kvnjuk konvertltatni gy, hogy a mezszlessg 6 s a pontossg 2 legyen.

280

SZABVNY, MAGAS SZINT BEMENET, KIMENET

A pontossg specifikcik a kvetkezk: Pontossg .* nincs megadva Hatsa a kimenetre Lsd elbbre! rvnybe lpnek a tpuskaraktertl fgg alaprtelmezs szerinti rtkek. Ezek: 1 : d, i, o, u, x, X esetn, 6 : e, E, f tpuskaraktereknl, minden szignifikns szmjegy g s Gnl,

s tpuskarakternl a teljes karakterlnc megy a kimenetre s .0 a c tpuskarakterre nincs hatssal. Az e, E, f tpuskaraktereknl nem jelenik meg a tizedes pont. A d, i, o, u, x, X esetn pedig az alaprtelmezs szerinti pontossg lp rvnybe (1). Ha ilyenkor a kirand paramter rtke radsul zrus is, akkor csak egyetlen szkz jelenik meg. .n A printf legfeljebb n karaktert, vagy decimlis helyirtket jelentet meg. Ha a kimenet n-nl tbb karakterbl ll, akkor csonkul, vagy kerekti a rutin a vonatkoz tpuskaraktertl fggen: d, i, o, u, x s X esetn legalbb n szmjegy jelenik meg. Ha a kimenet nnl kevesebb jegybl ll, akkor balrl zrus feltlts trtnik. Ha a kimeneti nnl tbbjegy, akkor sem csonkul. e, E, fnl a printf n szmjegyet jelentet meg a tizedes ponttl jobbra. Ha szksges, a legalacsonyabb helyirtken kerekts lesz. g, G esetn legfeljebb n szignifikns jegy jelenik meg. A c tpuskarakterre nincs hatsa.

Az s tpuskarakternl legfeljebb n karakter jelenik meg, azaz a hosszabb karakterlnc csonkul.

C programnyelv

281

Legvgl nzzk mg a h, l s L mretmdost karaktereket! A mretmdostk annak a paramternek a hosszt mdostjk, melyre az egsz formtumspecifikci vonatkozik. A d, i, o, u, x s X tpuskarakterekkel kapcsolatban csak a h s az l mretmdostk megengedettek. Jelentsk: h esetn a vonatkoz paramtert a printf tekintse short intnek, l esetn pedig long intnek. Az e, E, f, g, s G tpuskarakterekkel kapcsolatban csak az l s az L mretmdostk megengedettek. Jelentsk: l esetn a vonatkoz paramtert a printf tekintse doublenek, Lnl pedig long doublenek. Jelentessk meg a 2003. mrcius 2. dtumot HHNN alakban!
printf(%04d-%02d-%02d, 2003, 3, 2); printf(%.4d-%.2d-%.2d, 2003, 3, 2);

Mindkt hvs 20030302t szolgltat. Szemlltessk a 0, a #, a + s a jelzk hatst d, o, x, e s f tpuskarakterek esetn!


/* PELDA33.C: A printf jelzinek szemlltetse nhny tpuskarakterre. */ #include <stdio.h> #include <string.h> #define E 555 #define V 5.5 int main(void){ int i,j,k,m; char prefix[7], format[100], jelzok[]=" 0# + -", *tk[]={"6d", "6o", "8x", "10.2e", "10.2f"}; #define NJ (sizeof(jelzok)-2)*2 #define NTK sizeof(tk)/sizeof(tk[0]) printf("prefix 6d 6o 8x" " 10.2e 10.2f\n" "------+-------+-------+-----" "----+-----------+-----------+\n"); for(i=NJ-1; i>=0; --i){ strcpy(prefix, "%"); for(j=k=1; k<NJ; k<<=1) if(i&k) prefix[j++]=jelzok[k]; prefix[j]=0;

Az i 15rl indul, s zrusig cskken egyesvel, azaz ekzben lerja az sszes lehetsges ngybites bitkombincit. A k felvett rtkei 1, 2, 4 s 8, s a jelzok tmb pp ezen index elemeiben tallhatk meg a jelz karakterek.
strcpy(format, "%5s |"); for(m=0; m<NTK; ++m){

282

SZABVNY, MAGAS SZINT BEMENET, KIMENET

strcat(format, prefix); strcat(format, tk[m]); strcat(format, " |"); } strcat(format, "\n"); printf(format, prefix, E, E, E, V, V); } return(0); }

A program futtatsakor megjelen kimenet:


prefix 6d 6o 8x 10.2e 10.2f ------+-------+-------+---------+-----------+-----------+ %0#+- |+555 |01053 |0x22b |+5.50e+000 |+5.50 | %#+- |+555 |01053 |0x22b |+5.50e+000 |+5.50 | %0+- |+555 |1053 |22b |+5.50e+000 |+5.50 | %+- |+555 |1053 |22b |+5.50e+000 |+5.50 | %0#- |555 |01053 |0x22b |5.50e+000 |5.50 | %#- |555 |01053 |0x22b |5.50e+000 |5.50 | %0- |555 |1053 |22b |5.50e+000 |5.50 | %- |555 |1053 |22b |5.50e+000 |5.50 | %0#+ |+00555 |001053 |0x00022b |+5.50e+000 |+000005.50 | %#+ | +555 | 01053 | 0x22b |+5.50e+000 | +5.50 | %0+ |+00555 |001053 |0000022b |+5.50e+000 |+000005.50 | %+ | +555 | 1053 | 22b |+5.50e+000 | +5.50 | %0# |000555 |001053 |0x00022b |05.50e+000 |0000005.50 | %# | 555 | 01053 | 0x22b | 5.50e+000 | 5.50 | %0 |000555 |001053 |0000022b |05.50e+000 |0000005.50 | % | 555 | 1053 | 22b | 5.50e+000 | 5.50 |

int sprintf(char *puffer, const char *format<, parameter, ...>); A fggvny ugyanazt teszi, mint a printf, de a kimenett nem az stdout ra kszti, hanem az els paramterknt megkapott karaktertmbbe \0 lal lezrva. A vfprint, a vprintf s a vsprintf rutinokat mr megemltettk a Vltoz paramterlista fejezetben! Megoldand feladatok: Ksztsen char * kozepre(char *mit, int szeles) fggvnyt, mely a sajt helyn kzpre igaztja a mit karakterlncot szeles szlessgben, s viszszaadja az eredmny lnc kezdcmt! A kzpre igaztst csak szelesnl rvidebb lncok esetben kell elvgezni. A ktoldali prnz karakter indulsknt lehet szkz, de lehessen ezt fordtsi idben vltoztatni! rjon szoftvert, mely igaztott tblzatot hoz ltre az albbi tartalm TABLA fjl
Szveg Forint Egsz Papadopulosz 111222.3 1456 Sodik_sor 2.2 345

C programnyelv szabvny bemenetknti tirnytsval. Az eredmny tblzat:

283

+------------------------+-------------------+------------+ | Szveg | Forint | Egsz | +------------------------+-------------------+------------+ | Papadopulosz | 111222.30Ft | 1456 | +------------------------+-------------------+------------+ | Sodik_sor | 2.20Ft | 345 | +------------------------+-------------------+------------+

, ahol az els oszlop balra-, a msodik jobbra-, s a harmadik kzpre igaztott. A tbla egy sornak szerkezete:
| MEZO1| MEZO2Ft | MEZO3 |

, ahol MEZO1, MEZO2 s MEZO3 brutt adatszlessgek a mutatott mdon. Fokozhatja mg a feladatot gy, hogy az adatokat lehessen billentyzetrl is megadni! 11.9 Egyb fggvnyek Csak lezrt fjlokkal foglalkozik a kvetkez kt fggvny. A int remove(const char *fajlnev); trli az akr komplett ttal megadott azonostj fjlt. Sikeres esetben zrust, msklnben -1-et szolgltat a rutin. A int rename(const char *reginev, const char *ujnev); a reginev azonostj fjlt tnevezi ujnevre. Ha az ujnev meghajtnevet is tartalmaz, akkor az nem trhet el attl, ahol a reginev azonostj fjl elhelyezkedik. Ha viszont az ujnev a fjl eredeti helytl eltr utat tartalmaz, akkor az tnevezsen tl megtrtnik a fjl tmozgatsa is. A fggvny egyik paramtere sem lehet globlis fjlazonost! Sikeres esetben zrust szolgltat a rutin. A problmt a -1 visszaadott rtk jelzi. FILE *tmpfile(void); A rutin wb+ mddal ideiglenes fjlt hoz ltre, melyet lezrsakor, vagy normlis programbefejezdskor automatikusan trl a rendszer. A visszaadott rtk az ideiglenes fjl FILE struktrjra mutat, ill. NULL jn ltrehozsi problma esetn. char *tmpnam(char s[L_tmpnam]);

284

SZABVNY, MAGAS SZINT BEMENET, KIMENET

tmpnam(NULL) mdon hvva olyan fjlazonostt kapunk, mely egyetlen ltez fjl nevvel sem egyezik. A szolgltatott mutat bels, statikus karaktertmbt cmez, ahol a fjlazonost karakterlnc tallhat. A fjlazonost karakterlnc nem marad ott rkk, mert a kvetkez tmpnam hvs fellrja. Nem NULL mutatval hvva a rutin kimsolja a fjlazonostt az s karaktertmbbe, s ezzel is tr vissza. Az s tmb legalbb L_tmpnam mret kell, legyen. Tbbszri hvssal legalbb TMP_MAX darab, klnbz fjlazonost generlsa garantlt. Vigyzat! A fggvny fjlazonostkat generl s nem fjlokat!

C programnyelv

285

12 IRODALOMJEGYZK
[1] Kiss J. Raffai M. Szijrt M. Szrnyi M.: A szmtstechnika alapjai NOVADAT Bt., Gyr, 2001 [2] Marton L. Pukler A. Pusztai P.: Bevezets a programozsba NOVADAT Bt., Gyr, 1993 [3] Marton Lszl: Bevezets a Pascal nyelv programozsba NOVADAT Bt., Gyr, 1998 [4] B. W. Kernighan D. M. Ritchie: A C programozsi nyelv Mszaki Knyvkiad, Budapest, 1985 [5] B. W. Kernighan D. M. Ritchie: A C programozsi nyelv, az ANSI szerint szabvnyostott vltozat Mszaki Knyvkiad, Budapest, 1996 [6] Benk Tiborn Benk Lszl Tth Bertalan: Programozzunk C nyelven ComputerBooks, Budapest, 1999 [7] Benk Tiborn Urbn Zoltn: IBM PC programozsa TURBO C nyelven 2.0 BME Mrnktovbbkpz Intzet, Budapest, 1989

286

IRODALOMJEGYZK

13 TARTALOMJEGYZK
BEVEZETS ............................................................................................................ 2 JELLSEK............................................................................................................. 4 ALAPISMERETEK.................................................................................................. 5 3.1 Forrsprogram................................................................................................... 5 3.2 Fordts ............................................................................................................. 5 3.3 Kapcsolszerkeszts (link).............................................................................. 9 3.4 Futtats............................................................................................................ 10 3.5 Tblzat ksztse ........................................................................................... 10 3.6 Bemenet, kimenet............................................................................................ 19 3.7 Tmbk ........................................................................................................... 26 3.8 Fggvnyek..................................................................................................... 29 3.9 Prodzsekt......................................................................................................... 32 3.10 Karaktertmb s karakterlnc ......................................................................... 35 3.11 Loklis, globlis s bels, kls vltozk ....................................................... 39 3.12 Inicializls ..................................................................................................... 44 4 TPUSOK S KONSTANSOK .............................................................................. 47 4.1 Elvlaszt-jel................................................................................................... 48 4.2 Azonost ........................................................................................................ 49 4.3 Tpusok s konstansok a nyelvben .................................................................. 50 4.3.1 Egsz tpusok s konstansok ................................................................... 53 4.3.2 Felsorols (enum) tpus s konstans........................................................ 57 4.3.3 Vals tpusok s konstans ....................................................................... 60 4.3.4 Karakter tpus s konstans....................................................................... 62 4.4 Karakterlnc (string literal):............................................................................ 67 4.5 Deklarci ....................................................................................................... 70 4.5.1 Elemi tpusdefinci (typedef)................................................................. 74 5 MVELETEK S KIFEJEZSEK ........................................................................ 76 5.1 Aritmetikai mveletek (+, -, *, / s %)............................................................ 78 5.1.1 Multiplikatv opertorok (*, / s %) ........................................................ 79 5.1.2 Additv opertorok (+ s -)...................................................................... 82 5.1.3 Matematikai fggvnyek......................................................................... 82 5.2 Relci opertorok ( >, >=, <, <=, == s !=)................................................... 84 5.3 Logikai mveletek ( !, && s ||)...................................................................... 85 5.4 Implicit tpuskonverzi s egszellptets .................................................. 87 5.5 Tpusmdost szerkezet................................................................................. 89 5.6 sizeof opertor................................................................................................. 90 5.7 Inkrementls (++), dekrementls (--) s mellkhats .................................. 90 5.8 Bit szint opertorok ( ~, <<, >>, &, ^ s |) .................................................... 92 5.9 Feltteles kifejezs ( ? :).................................................................................. 96 5.10 Hozzrendels opertorok............................................................................... 97 5.11 Hozzrendelsi konverzi ............................................................................... 99 5.12 Vessz opertor............................................................................................. 101 5.13 Mveletek prioritsa ..................................................................................... 102 6 UTASTSOK ..................................................................................................... 106 6.1 sszetett utasts ........................................................................................... 106 6.2 Cmkzett utasts ......................................................................................... 107 6.3 Kifejezs utasts........................................................................................... 107 1 2 3

C programnyelv

287

6.4 Szelekcis utastsok..................................................................................... 108 6.5 Itercis utastsok........................................................................................ 111 6.6 Ugr utastsok ............................................................................................. 116 7 ELFELDOLGOZ (PREPROCESSOR)........................................................... 119 7.1 res (null) direktva ...................................................................................... 120 7.2 #include direktva.......................................................................................... 121 7.3 Egyszer #define makr................................................................................ 121 7.4 Elredefinilt makrk ................................................................................... 123 7.5 #undef direktva ............................................................................................ 123 7.6 Paramteres #define direktva ....................................................................... 124 7.7 Karaktervizsgl fggvnyek (makrk)........................................................ 125 7.8 Feltteles fordts .......................................................................................... 128 7.8.1 A defined opertor ................................................................................ 130 7.8.2 Az #ifdef s az #ifndef direktvk ......................................................... 130 7.9 #line sorvezrl direktva.............................................................................. 131 error direktva ........................................................................................................... 132 pragma direktvk ..................................................................................................... 132 8 OBJEKTUMOK S FGGVNYEK.................................................................. 133 8.1 Objektumok attribtumai .............................................................................. 133 8.1.1 Trolsi osztlyok ................................................................................. 134 8.1.2 lettartam (lifetime, duration)............................................................... 140 8.1.3 Hatskr (scope) s lthatsg (visibility) ............................................ 142 8.1.4 Kapcsolds (linkage)........................................................................... 144 8.2 Fggvnyek................................................................................................... 146 8.2.1 Fggvnydefinci ................................................................................ 147 8.2.2 Fggvny prototpusok.......................................................................... 152 8.2.3 Fggvnyek hvsa s paramterkonverzik......................................... 155 8.2.4 Nem szabvnyos mdostk, hvsi konvenci..................................... 157 8.2.5 Rekurzv fggvnyhvs........................................................................ 159 9 MUTATK........................................................................................................... 162 9.1 Mutatdeklarcik ........................................................................................ 162 9.1.1 Cm opertor (&)................................................................................... 163 9.1.2 Indirekci opertor (*) .......................................................................... 164 9.1.3 void mutat ........................................................................................... 165 9.1.4 Statikus s loklis cmek ....................................................................... 166 9.1.5 Mutatdeklartorok ............................................................................... 166 9.1.6 Konstans mutat.................................................................................... 167 9.2 Mutatk s fggvnyparamterek ................................................................. 168 9.3 Tmbk s mutatk....................................................................................... 169 9.3.1 Index opertor ....................................................................................... 171 9.3.2 Tmbdeklartor s nem teljes tpus tmb............................................ 173 9.4 Mutataritmetika s konverzi...................................................................... 175 9.4.1 sszeads, kivons, inkrementls s dekrementls ........................... 175 9.4.2 Relcik ................................................................................................ 176 9.4.3 Feltteles kifejezs ................................................................................ 177 9.4.4 Konverzi.............................................................................................. 178 9.5 Karaktermutatk............................................................................................ 179 9.5.1 Karakterlnc kezel fggvnyek........................................................... 179 9.5.2 Vltoz paramterlista .......................................................................... 185 9.6 Mutattmbk............................................................................................... 187

288

IRODALOMJEGYZK

9.7 Tbbdimenzis tmbk................................................................................. 189 9.7.1 Vletlenszm genertor......................................................................... 191 9.7.2 Dinamikus memriakezels .................................................................. 193 9.8 Tmbk, mint fggvnyparamterek ............................................................ 198 9.9 Parancssori paramterek ............................................................................... 200 9.9.1 Programbefejezs .................................................................................. 203 9.10 Fggvny (kd) mutatk ............................................................................... 205 9.10.1 atexit fggvny...................................................................................... 207 9.10.2 Tpusnv................................................................................................ 210 9.11 Tpusdefinci (typedef)................................................................................ 211 9.12 Ellenrztt bemenet ...................................................................................... 213 10 STRUKTRK S UNIK ............................................................................ 218 10.1 Struktradeklarci ....................................................................................... 219 10.1.1 Tpusdefinci ....................................................................................... 222 10.2 Struktratag deklarcik ............................................................................... 222 10.3 Struktrk inicializlsa ................................................................................ 224 10.4 Struktratagok elrse................................................................................... 225 10.5 Struktrk s fggvnyek.............................................................................. 230 10.6 nhivatkoz struktrk s dinamikus adatszerkezetek................................. 236 10.7 Struktra trillesztse.................................................................................... 243 10.8 UNIK.......................................................................................................... 244 10.8.1 Unideklarcik .................................................................................... 246 10.9 Bitmezk (bit fields) ..................................................................................... 247 10.10 Balrtk jobbrtk .................................................................................. 250 10.11 Nvterletek .............................................................................................. 251 11 MAGAS SZINT BEMENET, KIMENET ..................................................... 254 11.1 Folyamok megnyitsa ................................................................................... 254 11.2 Folyamok pufferezse ................................................................................... 256 11.3 Pozcionls a folyamokban.......................................................................... 258 11.4 Bemeneti mveletek...................................................................................... 260 11.5 Kimeneti mveletek ...................................................................................... 262 11.6 Folyamok lezrsa......................................................................................... 263 11.7 Hibakezels ................................................................................................... 263 11.8 Elre definilt folyamok ............................................................................... 267 11.8.1 Bemenet az stdin-rl ............................................................................. 269 11.8.2 Kimenet az stdout-ra ............................................................................. 274 11.9 Egyb fggvnyek......................................................................................... 283 12 IRODALOMJEGYZK.................................................................................... 285

You might also like