Download as doc, pdf, or txt
Download as doc, pdf, or txt
You are on page 1of 207

Elsz a magyar kiadshoz A C programnyelvet eredetileg a Bell Laboratriumban az UNIX opercis rendszerhez, az alatt fejlesztettk ki PDP-11_ szmtgpen.

A kifejlesztse ta eltelt vek sorn bebizonyosodott, hogy a C nem egyszeren egy a napjainkban gombamd szaporod programnyelvek kzl. Korszer vezrlsi s adatszerkezetei, rugalmassga, knny elsajtthatsga szles alkalmazsi terletet biztostott szmra, klnsen a 16 bit-es mikroprocesszorok megjelense ta rendkvl sok gpen dolgoznak C nyelven. C fordt kszlt olyan gpekre, mint az IBM System/370, a Honeywell 6000 s az Interdata 8/32. A nyelv a kutats-fejlesztsi, tudomnyos cl programozs npszer eszkzv vlt. Magyarorszgon szintn egyre tbb olyan szmtgp mkdik, amely alkalmas a C megvalstsra. Ilyenek a hazai gyrtmnyok kzl a TPA-11 sorozat, az R-11 , a szocialista gyrtmnyok kzl az SZM-4 szmtgpek, de meg kell emltennk a haznkban ugyancsak elterjedt PDP-11 sorozat tagjait is. Igy rtheten a magyar szmtstechnikai szakemberek krben mind nagyobb az rdeklds a C nyelv irnt, egyre tbben szeretnnek megtanulni programozni ezen a nyelven. Ebben szeretnnk segteni e knyv megjelentetsvel, amely didaktikusan, b plda- s gyakorlatanyaggal kiegsztve szl a C sszetevirl, jellemzirl, de tartalmazza a nyelv referenciakziknyvt is. Az Olvas a legavatottabb forrsbl merthet : a vilghr szerzpros egyik tagja, Dennis Ritchie a C nyelv tervezje, a msik, Brian W. Kernighan tbb, magyarul is megjelent nagy siker szakknyv szerzje. Remljk, mind a kezd, mind a gyakorlott C-programozk haszonnal forgatjk majd a mvet. A Kiad Elsz A C ltalnos cl programnyelv. Tmrsg, a korszer vezrlsi s adatstruktrk hasznlata, bsges opertorkszlet jellemzi. Nem nevezhet sem nagyon magas szint, sem nagy nyelvnek, s nem ktdik egyetlen specilis alkalmazsi terlethez sem. Ugyanakkor a megktsek hinya, az ltalnos jelleg sok magas szint nyelvnl knyelmesebb s hatkonyabb teszi. A C nyelvet tervezje, Dennis Ritchie eredetileg az UNIX opercis rendszer programnyelvnek sznta. Az opercis rendszer, a C fordt s lnyegben az sszes UNIX alkalmazsi program (a knyv eredetijnek a nyomdai elksztshez hasznlt szoftver is) C nyelven rdott. Dennis Ritchie az els C fordtt PDP- 11-en rta meg, de azta nhny ms gpre, gy az IBM System/370-re, a Honeywell 6000-re s az Interdata 8/32-re is kszlt C fordt: A C nyelv nem ktdik szorosan egyetlen hardverhez vagy rendszerhez sem, knnyen rhatunk olyan programokat, amelyek vltoztats nlkl futnak brmely ms, a C nyelvet tmogat gpen. Knyvnkkel a C nyelv programozs elsajttshoz szeretnnk segtsget adni. Az olvas mr az Alapismeretek c. fejezet megrtse utn elkezdhet programozni. A knyv ezutn kln-kln fejezetben ismerteti a C nyelv f sszetevit, majd referencia-kziknyv formjban is sszefoglalja a nyelvet. Az anyag tlnyomrszt pldaprogramok rsbl, olvassbl s mdostsbl ll, nem szraz szablygyjtemnyt adunk az olvas kezbe. A legtbb plda teljes, ellenrztt, mkdkpes program, s nem csupn elszigetelt programrsz. Knyvnkben nemcsak a nyelv hatkony hasznlatt

kvntuk ismertetni. Trekedtnk arra is, hogy j stlus, ttekinthet, hasznos algoritmusokat s programozsi elveket mutassunk be. A knyv nem bevezet jelleg programozsi segdknyv; felttelezi, hogy az olvas ismeri a programozs olyan alapfogalmait, mint: vltozk, rtkad utastsok, ciklusok, fggvnyek. [A C nyelvben hasznlatos terminolgia szerint a szubrutinokat fggvnyeknek (functions) nevezik. A ford.] Ugyanakkor a knyv alapjn egy kezd programoz is megtanulhatja a nyelvet, br szksge lehet jrtasabb kollga segtsgre. Tapasztalataink szerint a C sokfle feladat megfogalmazsra alkalmas, kellemes, kifejez s rugalmas nyelv. Knnyen elsajtthat, s aki megismerte, szvesen hasznlja. Remljk, hogy knyvnk segtsget nyjt a nyelv hatkony hasznlatban. A knyv megszletshez s a megrsakor rzett rmnkhz nagyban hozzjrultak bartaink, kollgink gondolatgazdag brlatai s javaslatai. Klnsen hlsak vagyunk Mike Bianchinak, Jim Blue-nak, Stu Feldmannek, Doug Mcllroynak, Bill Roome-nak, Bob Rosinnek s Larry Roslernek, akik figyelmesen elolvastk a knyv tbb vltozatt is. Al Aho, Steve Bourne, Dan Dvorak, Chuck Haley, Debbie Haley, Marion Harris, Rick Holt, Steve Johnson, John Mashey, Bob Mitze, Ralph Muha, Peter Nelson, Elliot Pinson, Bill Plauger, Jerry Spivack, Ken Thompson s Peter Weinberger megjegyzseikkel sokat segtettk munknkat klnbz fzisaiban. Ksznet illeti tovbb Mike Lesket s Jim Ossant a knyv szedsben val rtkes kzremkdskrt. Brian W. Kernighan Dennis M. Ritchie Bevezets A C ltalnos cl programnyelv. Trtnetileg szorosan kapcsoldik az UNIX opercis rendszerhez, mivel ezen rendszer alatt fejlesztettk ki s mivel az UNIX s szoftvere C nyelven kszlt. A nyelv azonban nem ktdik semmilyen opercis rendszerhez vagy gphez. Noha _ rendszerprogramnyelvnek szoks nevezni, mivel opercis rendszerek rsban jl hasznlhat, ugyanolyan clszeren alkalmazhat nagyobb numerikus, szvegfeldolgoz s adatbzis-kezel programok esetben is. A C viszonylag alacsony szint nyelv. Ez nem lebecslst jelent, csupn azt, hogy a C nyelv - mint a legtbb szmtgp karakterekkel, szmokkal s cmekkel dolgozik. Ezek kombinlhatk, s az adott gpen rendelkezsre ll aritmetikai s logikai opertorokkal mozgathatk. A C nyelvben nincsenek olyan mveletek, amelyekkel sszetett objektumokat, pl. karakterlncokat, halmazokat, listkat vagy tmbket egy egsznek tekinthetnnk. Hinyzik pldul azoknak a PL/1 mveleteknek a megfelelje, amelyek egy egsz tmbt vagy karakterlncot kezelnek. A nyelvben nincs ms trfoglalsi lehetsg, mint a statikus definci s a fggvnyek loklis vltozinl alkalmazott verem elv. Nincs tovbb olyan, hulladk trterletek sszegyjtsre alkalmas mechanizmus (garbage collection), mint amilyet az ALGOL 68 nyjt. Vgl pedig maga a C nyelv nem biztost be- s kiviteli szolgltatsokat: nincsenek read s write utastsok, sem rgztett llomnyelrsi (file-elrsi) mdszerek. Az sszes ilyen magasabb szint tevkenysget explicit mdon hvott fggvnyekkel kell megvalstani. Hasonlkppen a C nyelv csak egyszer, egy szlon fut vezrlstadsi struktrkat tartalmaz: ellenrzseket, ciklusokat s alprogramokat, de nem teszi lehetv a multiprogramozst, a

prhuzamos mveleteket, a (korutinok) hasznlatt.

szinkronizlst

vagy

prhuzamos

rutinok

Br e szolgltatsok nmelyiknek hinya slyos hinyossgnak tnhet, a nyelv szerny mretek kz szortsa valjban elnykkel jrt. A C nyelv viszonylag kicsi, ezrt kis helyen lerhat s gyorsan elsajtthat. A C fordt egyszer s tmr lehet, ugyanakkor knnyen megrhat: a jelenlegi technolgival egy j gpen fut fordt nhny hnap alatt elkszthet, s kdjnak 80%-a vrhatlag kzs lesz a mr ltez fordtk kdjval. Ez nagyban segti a nyelv terjedst, a programok cserjt. Mivel a C nyelvben definilt adattpusokat s vezrlsi szerkezeteket a legtbb mai szmtgp kzvetlenl tmogatja, kicsi lesz az nll programok megvalstshoz futsi idben szksges rutinknyvtr, amely a PDP11 -en pldul csak a 32 bit-es szorzst s ; osztst vgrehajt rutinokat, illetve a szubrutinba val belpst s az onnan val kilpst szolgl szekvencikat tartalmazzk. Termszetesen a nyelv valamennyi megvalstst kiterjedt, az adott gphez illeszked fggvnyknyvtr teszi teljess. A fggvnyek biztostjk a be- s kiviteli mveletek elvgzst, a karakterlncok kezelst s a trfoglalsi mveletek vgrehajtst. Mivel azonban csak explicit mdon hvhatk, szksg esetn elhagyhatk, ezenkvl C programknt gpfggetlen mdon is megrhatk. Minthogy a C a mai szmtgpek kpessgeit tkrzi, a C nyelv programok ltalban elg hatkonyak ahhoz, hogy ne kelljen helyettk assembly programokat rni. Ennek legjellemzbb pldja maga az UNIX opercis rendszer, amely majdnem teljes egszben C nyelven rdott. 13000 sornyi rendszerkdbl csak a legalacsonyabb szinten elhelyezked 800 sor kszlt assemblyben. Ezenkvl majdnem minden UNIX alkalmazsi szoftver forrsnyelve is a C; az UNIX felhasznlk tlnyom tbbsge (belertve e knyv szerzinek egyikt is) nem is ismeri a PDP-11 assembly nyelvet. A C nyelv sok szmtgp kpessgeihez illeszkedik, minden konkrt szmtgp-architektrtl fggetlen, gy knnyen rhatunk gpfggetlen, teht olyan programokat, amelyek klnfle hardvereken vltoztats nlkl futtathatk. A szerzk krnyezetben ma mr szokss vlt, hogy az UNIX alatt kifejlesztett szoftvert tviszik a helyi Honeywell, IBM s Interdata rendszerekre. Valjban az ezen a ngy gpen mkd C fordtk s futtatsi szolgltatsok egymssal sokkal inkbb kompatibilisek, mint az ANSI-szabvnyos FORTRAN megfelel vltozatai. Maga az UNIX opercis rendszer jelenleg mind a PDP-11-en, mind pedig az Interdata 8/32-n fut. Azokon a programokon kvl, amelyek szksgszeren tbb-kevsb gpfggek, mint a C fordt, az assembler vagy a debugger, a C nyelven rt szoftver mindkt gpen azonos. Magn az opercis rendszeren bell az assembly nyelv rszeken s a perifriahandlereken kvli 7000 sornyi kd mintegy 95%-a azonos. Ms nyelveket mr ismer programozk szmra az sszehasonlts kedvrt rdemes megemlteni a C nyelv nhny trtneti, technikai s filozfiai vonatkozst. A C nyelv sok alaptlete a nla jval rgebbi, de mg ma is l BCPL nyelvbl szrmazik, amelyet Martin Richards fejlesztett ki. A BCPL a C nyelvre kzvetett mdon, a B nyelven keresztl hatott, amelyet Ken Thompson 1970-ben a PDP-7-esen fut els UNIX rendszer szmra rt. Br a C nyelvnek van nhny kzs vonsa a BCPL-lel, mgsem nevezhet semmilyen rtelemben a BCPL egyik vltozatnak. A BCPL s a B tpus nlkli nyelvek: az egyetlen adattpus a gpi sz s msfle objektumokhoz val hozzfrs specilis opertorokkal s fggvnyhvsokkal trtnik.

A C nyelvben az alapvet adatobjektumok a karakterek, a klnfle mret egsz (integer) tpusok s a lebegpontos szmok. Ehhez jrul mg a szrmaztatott adattpusok hierarchija, amelyek mutatk (pointerek), tmbk, struktrk, unionok s fggvnyek formjban hozhatk ltre. A C nyelv tartalmazza a jl struktrlt programok ksztshez szksges alapvet vezrlsi szerkezeteket: az sszetartoz utastssorozatot, a dntshozatalt (if), a programhurok tetejn (while for) vagy aljn (do) vizsglatot tartalmaz ciklust s a tbb eset valamelyiknek kivlasztst (switch). (Ezek mindegyike rendelkezsre llt a BCPL-ben is, szintaxisuk azonban nmileg klnbztt a C-belitl; a BCPL nhny vvel megelzte a struktrlt programozs elterjedst.) A C nyelv lehetv teszi a mutatk hasznlatt s a cmaritmetikt. A fggvnyek argumentumainak tadsa az argumentum rtknek lemsolsval trtnik, s a hvott fggvny nem kpes megvltoztatni az aktulis argumentumot a hvban. Ha nv szerinti hvst akarunk megvalstani, egy mutatt adhatunk t explicit mdon s a fggvny megvltoztathatja azt az objektumot, amire a mutat mutat. A tmbnv gy addik t, mint a tmb kezdcme, teht tmbargumentumok tadsa nv szerinti hvssal trtnik. Brmely fggvny rekurzv mdon hvhat s loklis vltozi rendszerint automatikusak, azaz a fggvny minden egyes meghvsakor jra ltrejnnek. A fggvnydefincik nem skatulyzhatk egymsba, a vltozk azonban blokkstruktrban is deklarlhatk. A C programokban szerepl fggvnyek kln is fordthatk. Megklnbztethetnk egy fggvnyre nzve bels, kls (csak egyetlen forrsllomnyban ismert) s teljesen globlis vltozkat. A bels vltozk automatikusak s statikusak lehetnek. Az automatikus vltozk a hatkonysg nvelse rdekben regiszterekbe helyezhetk, de a register deklarci csak ajnls a fordtnak s nem vonatkozik adott gpi regiszterekre. A PASCAL-lal vagy az ALGOL 68-cal sszehasonltva a C nem szoros tpusmegkts nyelv, viszonylag engedkeny az adatkonverzikat illeten, de az adattpusok konverzija nem a PL/1-re jellemz szabadossggal trtnik. A jelenlegi fordtk nem ellenrzik futs kzben a tmbindexeket, argumentumtpusokat stb. Ha szigor tpusellenrzs szksges, a C fordt egy specilis vltozatt, a lint-et kell hasznlni. A lint nem generl kdot, hanem a fordts s tlts sorn lehetsges legtbb szempontbl igen szigoran ellenriz egy adott programot. Jelzi a nem illeszked tpusokat, a kvetkezetlen argumentumhasznlatot, nem hasznlt vagy nyilvnvalan inicializlatlan vltozkat, az esetleges gpfggetlensgi problmkat stb. Azok a programok, amelyekben a lint nem tall hibt, ritka kivtelektl eltekintve krlbell ugyanolyan mrtkben mentesek a tpushibktl, mint pldul az ALGOL 68 programok. A megfelel helyen a lint tovbbi szolgltatsait is ismertetjk. Vgezetl a C-nek, mint minden ms nyelvnek, megvannak a maga gyengesgei. Nmelyik opertornak rossz a precedencija; a szintaxis bizonyos rszei jobbak is lehetnnek; a nyelvnek tbb, kismrtkben eltr vltozata l. Mindezzel egytt a C nyelv szles krben alkalmazhat, rendkvl hatkony s kifejezkpes nyelvnek bizonyult. A knyv felptse a kvetkez: Az 1. fejezet a nyelv megtanulst segt bevezets a C nyelv kzponti rszbe. Ennek az a clja, hogy az olvas minl hamarabb elkezdhessen programozni, mivel a szerzk hite szerint egy j nyelv megtanulsnak egyetlen mdja, ha programokat runk az illet nyelven. A fejezet felttelezi, hogy az

olvas rendelkezik a programozs alapjainak aktv ismeretvel; az anyag nem magyarzza meg, hogy mi a szmtgp, mi a fordts, sem pedig az olyan kifejezsek jelentst, mint n = n + 1 . Br lehetleg mindentt hasznos programozsi mdszereket prbltunk bemutatni, nem szntuk mvnket az adatstruktrk s algoritmusok kziknyvnek: knyszer vlaszts esetn elssorban a nyelvre koncentrltunk. A 2. ... 6. fejezet rszletesen, az 1. fejezetnl preczebben trgyalja a C nyelv klnbz elemeit, a hangsly azonban itt sem a rszleteken, hanem a teljes, a gyakorlatban alkalmazhat pldaprogramokon van. A 2. fejezet az alapvet adattpusokat, opertorokat s kifejezseket ismerteti. A 3. fejezet a programvezrlssel: if-else, while, for stb. foglalkozik. A 4. fejezet tmi : a fggvnyek s a program felptse, kls vltozk, az rvnyessgi tartomny szablyai stb. Az 5. fejezet a mutatkkal s a cmaritmetikval, a 6. fejezet a struktrkkal s unionokkal kapcsolatos tudnivalkat tartalmazza. A 7. fejezet a szabvnyos bes kiviteli (I/o) knyvtrat ismerteti, amely kzs csatlakozfelletet kpez az opercis rendszer fel. Ezt a be- s kiviteli knyvtrat minden olyan gp tmogatja, amely a C-t tmogatja, teht azok a programok, amelyek ezt hasznljk bevitel, kivitel s ms rendszerfunkcik cljbl, lnyegben vltoztats nlkl vihetk t egyik rendszerrl a msikra. A 8. fejezet a C programok s az UNIX opercis rendszer kztti csatlakozsokat rja le, elssorban a be- s kivitelre, az llomnyrendszerre s a gpfggetlensgre koncentrlva. Br e fejezet egy rsze UNIX-specifikus, a nem UNIX-ot hasznl programozk is hasznos tudnivalkat tallhatnak benne - megtudhatjk pl., hogyan valstottk meg a szabvnyos knyvtr adott verzijt, s hogyan nyerhetnk gpfggetlen programkdot. Az A. fggelk a C nyelv referencia-kziknyvt, a C szintaxisnak s szemantikjnak hivatalos lerst tartalmazza. Ha az elz fejezetekben esetleg ktrtelmsgekre vagy hinyossgokra bukkanunk, mindig ezt kell vgs dntbrnak tekinteni. Mivel a C olyan, mg fejldsben lev nyelv, amely szmos rendszeren fut, elfordulhat, hogy a knyv egy-egy rsze nem felel meg valamely adott rendszer fejldse pillanatnyi llapotnak. Igyekeztnk elkerlni az ilyen problmkat, s megprbltuk felhvni a figyelmet a lehetsges nehzsgekre. Ktes esetekben azonban ltalban a PDP11 UNIX rendszer esetben rvnyes helyzet lerst vlasztottuk, mivel a C programozk tbbsgnek ez a munkakrnyezete. Az A. fggelkben ismertetjk a fontosabb C rendszerek megvalstsaiban mutatkoz klnbsgeket is. _ A. fggelk : C referencia-kziknyv 1. Bevezets A kziknyv a DEC PDP 11 , a Honeywell 6000, az IBM System/370 s az Interdata 8/32 gpeken hasznlhat C nyelvet ismerteti. Eltrsek esetn a PDP 11 -es vltozatot helyezi eltrbe, de igyekszik rmutatni a megvalstsfgg rszletekre. Nhny kivteltl eltekintve ezek a gpfgg rszletek kzvetlenl a hardver alaptulajdonsgaibl kvetkeznek; a klnfle fordtk ltalban elgg kompatibilisek.

2. Szintaktikai egysgek A szintaktikai egysgek hat osztlyba sorolhatk: azonostk, kulcsszavak, llandk, karakterlncok, opertorok s egyb szepartorok. A szkzket, tabultorokat, jsorokat, megjegyzseket (kzs nevkn res helyeket), mint az albbiakban is ltni fogjuk, a C fordt nem veszi figyelembe, eltekintve attl, hogy feladatuk a szintaktikai egysgek elvlasztsa. res helyre van szksg az egybknt szomszdos azonostk, kulcsszavak s llandk elvlasztsra. Ha a beolvasott szveg szintaktikai egysgekre bontsa adott karakterig megtrtnt, a fordt azt a lehet leghosszabb karakterlncot tekinti a kvetkez egysgnek, amelyrl felttelezhet, hogy mg egyetlen szintaktikai egysget kpez. 2.1. Megjegyzsek A /*karakterek megjegyzst (comment) vezetnek be, amely a */ karakterekkel zrul. A megjegyzsek nem skatulyzhatk egymsba. 2.2. Azonostk (nevek) Az azonost betk s szmjegyek sorozata; az els karakter bet kell, hogy legyen. A alhzsjel betnek szmt. A nagy-s kisbetk klnbzk. Csupn az els nyolc karakter rtkes, br tbb is hasznlhat. A klnfle , assemblerek s betltprogramok ltal hasznlt kls azonostk ennl ktttebbek: DEC PDP 11 nagybet). Honeywell 6000 IBM 360/370 Interdata 8/32 7 karakter, ktfle bettpus (kis-s 6 karakter, egyfle bettpus. 7 karakter, egyfle bettpus. 8 karakter, ktfle bettpus.

2.3. Kulcsszavak Az albbi azonostk a nyelv kulcsszavai, gy egyb clra nem hasznlhatk: int extern else char register for float typedef do double static while struct goto switch union return case long sizeof default short break entry auto unsigned continue if Az entry kulcsszt egyetlen jelenleg mkd fordtban sem valstottk meg, ksbbi fejlesztsekhez tartottuk fenn. Bizonyos megvalstsokban a fortran s az asm szavak is kulcsszknt szerepelnek. 2.4. llandk Tbbfajta lland van; ezeket a kvetkezkben soroljuk fel. A mreteket rint hardverjellemzket a 2.6. pontban foglaljuk ssze. 2.4.1. Egsz llandk A szmjegyek sorozatt tartalmaz egsz tpus (integer) llandt a fordt oktlisnak tekinti, ha 0-val (a nulla szmjeggyel) kezddik, egybknt decimlisnak veszi. A 8 s 9 szmjegyek oktlis rtke 10, ill. 11 . Az olyan szmjegysorozatot, amelyet 0X vagy 0x (a 0 a nulla szmjegy) elz meg, a fordtprogram hexadecimlis egsznek tekinti. Hexadecimlis szmjegyek az a-tl, ill. A-tl f-ig, ill. F-ig elhelyezked karakterek, amelyeknek rtke 10, . . ., 15. Azt a

decimlis llandt, amelynek rtke meghaladja a gpenbr zolhat legnagyobb eljeles egszt, a fordtprogram long-nak veszi; hasonlkppen long lesz az az oktlis vagy hexadecimlis lland, amelynek rtke meghaladja a legnagyobb, eljel nlkli gpi egszt. 2.4.2. Explicit long llandk Az a decimlis, oktlis vagy hexadecimlis egsz, amelyet kzvetlenl l (el bet) vagy L kvet, long (hossz) lland. Amint arrl az albbiakban sz lesz, bizonyos gpeken az int s long rtkek azonosak. 2.4.3. Karakterllandk A karakterlland aposztrfok (szimpla idzjelek) kz zrt karakter, pl. x. A karakterlland rtke a karakternek a gp karakterkszletben szerepl numerikus rtke. Bizonyos nem grafikus karaktereket, pl. az aposztrfot () vagy a fordtott trtvonalat (\) az albbi escape-szekvencikkal brzolhatunk: jsor NL (LF) \n vzszintes tab HT \t vissza-szkz BS \b kocsi-vissza CR \r lapdobs FF \f fordtott trtvonal \ \\ aposztrf \ bitminta ddd \ddd A \ddd escape-szekvencia egy fordtott trtvonalat s 1 , 2 vagy 3 rkvetkez oktlis szmjegyet tartalmaz, amelyek a kvnt karakter rtkt hatrozzk meg. E konstrukci specilis esete a \0 (amit nem kvet szmjegy), amely a NULL karaktert jelli. Ha a fordtott trtvonalat kvet karakter nem az elbbiek egyike, a fordt a fordtott trtvonalat nem veszi figyelembe. 2.4.4. Lebegpontos llandk A lebegpontos lland egsz rszbl, tizedespontbl, trtrszbl, e-bl vagyE-bl s (esetleg eljeles) kitevbl ll. Mind az egsz, mind a trt rsz szmjegyek sorozata. Akr az egsz, akr a trt rsz hinyozhat (de mind a kett nem!); ill. a tizedespont vagy az e s a kitev kzl az egyik szintn elmaradhat. Minden lebegpontos lland duplapontossg. 2.5. Karakterlncok A karakterlnc idzjelek kz zrt karaktersorozat: . . .. A karakterlnc tpusa szerint karaktertmb, trolsi osztlya static (l. a kvetkezkben a 4. szakaszt), s a megadott karakterek inicializljk. Az egyes karakterlncok, mg az azonos mdon lertak is, kln egysget kpeznek. A fordt minden karakterlnc vgre elhelyezi a \0 nullabyte-ot abbl a clbl, hogy a karakterlncot vizsgl programok megtalljk a karakterlnc vgt. A karakterlncon bell elhelyezett idzjelet \ kell, hogy megelzze; a karakterllandknl ismertetett sszes escapeszekvencia hasznlhat. Vgl megjegyezzk, hogy az \-t s az azt kzvetlenl kvet jsort a fordt nem veszi figyelembe. 2.6. Hardverjellemzk Az albbi tblzatban nhny olyan hardvertulajdonsgot foglaltunk ssze, amely gprl gpre vltozik. Noha ezek a programok gpfggetlensgt rintik, mgis jval kisebb problmt okoznak, mint azt valaki eleve gondoln. (A szmokbitekben rtendk.)

DEC PDP-11 8/32 char int short long float double rtktartomny ASCII 8 16 16 32 32 64

Honeywell 6000 ASCII 9 36 36 36 36 72

IBM 370 ASCII 8 32 16 32 32 64

Interdata

EBCDIC 8 32 16 32 32 64

+-10+-38

+-10+-38

+-10+-76

+-10+-76

E ngy gp esetben a lebegpontos szmoknak 8 bites kitevjk van. 3. A szintaxis jellse A kziknyvben hasznlt szintaktikai jellsmdban a kulcsszavakat s karaktereket - ahol az egyrtelmsg megkvnja kvr szedssel jelljk. A vlaszthat (alternatv) kategrik kln sorban szerepelnek. Az elhagyhat (opcionlis) szimblumokat az opc index jelli, gy { kifejezsopc } kapcsos zrjelek kz zrt elhagyhat kifejezst jell. A szintaxist ksbb a 18. pontban foglaljuk ssze. 4. Az azonostk rtelmezse A C nyelv az azonostk rtelmezst az azonostk kt tulajdonsgra alapozza: a trolsi osztlyra s a tpusra. A trolsi osztly az azonosthoz rendelt trhely elhelyezkedst s lettartamt, a tpus az azonosthoz rendelt trterleten tallt rtkek jelentst hatrozza meg. Ngy deklarlhat trolsi osztly van: automatikus, statikus, kls s regiszterosztly. Az automatikus vltozk egy blokk minden hvsra nzve loklisak (l. a 9.2. pontot) rtkket a blokkbl val kilpskor elvesztik; a statikus vltozk egy blokkra nzve loklisak, de mg akkor is megtartjk rtkket, ha a vezrls idkzben kilpett a blokkbl; a kls vltozk megmaradnak s megtartjk rtkket az egsz program vgrehajtsa sorn s fggvnyek kztti kommunikcira hasznlhatk, mg kln-kln lefordtott fggvnyek esetben is. A regisztervltozk (ha lehetsges) a gp gyors regisztereiben troldnak; az automatikus vltozkhoz hasonlan az egyes blokkokra nzve loklisak s a blokkbl val kilpskor eltnnek. A C nyelv tbb alapvet objektumtpus hasznlatt engedi meg: A karakterknt (char) deklarlt objektumok elegenden nagyok ahhoz, hogy az adott implementci karakterkszletnek tetszleges elemt trolni tudjk, s ha valban egy, az illet karakterkszletbl vett karaktert akarunk karakter tpus vltozban trolni, annak rtke meg fog egyezni a karakter egsz rtk kdjval. Ms mennyisgek is trolhatk karakter tpus vltozkban, de ennek megvalstsa gpfgg. Maximum hromfle egsz tpus mret ll rendelkezsre, amelyeket short int (rvid egsz), int (egsz) s long int (hossz egsz) alakban deklarlunk. A hosszabb egszek bizonyosan nem ignyelnek kevesebb trhelyet, mint a rvidebbek, de az adott nyelvi megvalsts a short int-eket a long int-ekkel vagy akr mind a kettt kznsges egszekkel (int) egyenl mretv teheti. A kznsges egszeknek a befogad gp architektrjbl kvetkez

termszetes mretk kielgtsre szolgl.

van;

tbbi

mret

specilis

ignyek

Az unsigned-knt deklarlt, eljel nlkli egszekre a modulo Zn aritmetika szablyai vonatkoznak, ahol n a bitek szma az adott megvalstsban. (A PDP-11 az eljel nlkli long mennyisgeket nem tmogatja.) Az egyszeres pontossg lebegpontos (float) s a duplapontossg lebegpontos (double) brzols egyes gpeken azonos lehet. Mivel az emltett tpus objektumok clszeren rtelmezhetk szmokknt, ezekre mint aritmetikai tpusokra fogunk hivatkozni. Az sszes char s int tpust (mrettl fggetlenl) egyttesen integrlis tipusnak, a float-ot s a double-t egyttesen lebegpontos tipusnak fogjuk nevezni. Az alapvet aritmetikai tpusokon kivl elvileg vgtelen szm leszrmaztatott tpus kpezhet az alaptpusokbl, az albbi mdokon: tmbk, amelyek a legtbb tpus objektumbl kpezhetk; fggvnyek, amelyek adott tpus objektumot adnak vissza; mutatk, amelyek adott tpus objektumra mutatnak; struktrk, amelyek klnfle tpus objektumok tpus objektumok brmelyikt sorozatt tartalmazzk; unionok, amelyek tartalmazhatjk. klnfle

Az objektumok ltrehozsnak ezek a mdszerei ltalban rekurzv mdon alkalmazhatk. 5. Objektumok s balrtkek Az objektum a tr valamely mveletekkel kezelhet rsze; a balrtk (lvalue) objektumra hivatkoz kifejezs. A balrtk kifejezsre kzenfekv plda az azonost. Bizonyos opertorok balrtkeket eredmnyeznek: ha E mutat tpus kifejezs, akkor *E olyan balrtk kifejezs, amely arra az objektumra hivatkozik, amire az E mutat. A balrtk elnevezs az E1 =E2 rtkad kifejezsbl szrmazik, amelyben az E1 bal oldali operandusnak balrtk kifejezsnek kell lennie. Az egyes opertorok albb kvetkez ismertetse sorn kzljk hogy az adott opertor balrtk operandusokat vr-e s hogy balrtket ad-e eredmnyl. 6. Konverzik Operandusuktl fggen szmos opertor vlthatja ki valamelyik operandusa rtknek egyik tpusbl valamilyen msik tpusba trtn talaktst. Ebben a szakaszban az ilyen konverzik vrhat eredmnyt ismertetjk. A kznsges opertorok tbbsge ltal megkvetelt konverzikat a 6.6. pontban foglaltuk ssze; ezt szksg szerint az egyes opertorok trgyalsnl tovbbi informcikkal egsztettk ki. 6.1. Karakterek s egszek Karaktert s rvid _egszt mindentt hasznlhatunk, ahol kznsges egsz hasznlhat. Az rtk minden esetben int-t alakul. Rvidebb egsz hosszabb egssz trtn konvertlsa mindig eljel-kiterjesztssel jr: az egszek eljeles mennyisgek. Az adott gptl fgg, hogy karakterek esetben is trtnik-e eljel-kiterjeszts, de annyi bizonyos, hogy a

szabvnyos karakterkszlet valamennyi eleme nemnegatv. Azok kzl a szmtgpek kzl, amelyeket ez a kziknyv figyelembe vesz, csak a PDP- 11 vgez eljel-kiterjesztst. A PDP-11 -en a karakter tpus vltozk rtktartomnya -128 s 127 kztt van; az sszes ASCII karakter pozitv. Az oktlis escape-szekvencia segtsgvel megadott karakterllandkra eljel-kiterjeszts trtnik, s negatvknt is megjelenhetnek, pl. \077 rtke -1. Ha egy hosszabb egszt rvidebb egssz vagy char-r alaktunk, a levgs bal oldalon trtnik : a felesleges bitek egyszeren elmaradnak. 6.2. Float s double A C-ben mindenfle lebegpontos mvelet duplapontossg; amikor egy kifejezsben float fordul el, az a trt rsz nullkkal val kitltse rvn double-l hosszabbodik. Ha double-t kell float-t alaktani, pl. rtkads sorn, a double elszr kerektdik s csak ezutn rvidl float hosszsgv. 6.3. Lebegpontos s integrlis mennyisgek A lebegpontos rtkek integrlis tpusv alaktsa ltalban elgg gpfgg mvelet; klnskppen a negatv szmok csonktsnak irnya vltozik gprl gpre. Ha a rendelkezsre ll helyen az eredmny nem fr el, hatrozatlan lesz. Integrlis rtkek lebegpontoss alaktsa problmamentes. A pontossg nmileg cskken, ha a clhelyen nincs elegend bit. 6.4. Mutatk s egszek Az int vagy long int mennyisgek a mutatkhoz hozzadhatk vagy azokbl levonhatk; ebben az esetben az elbbiek az sszead opertornl lertak szerint alakulnak t. Kt, ugyanolyan tpust megcmz mutat egymsbl kivonhat: ez esetben az eredmny egssz alakul t, amint azt a kivon opertornl trgyaljuk. 6.5. Eljel nlkli egszek Ha eljel nlkli (unsigned) s kznsges egszeket kombinlunk, a kznsges egsz eljel nlkliv alakul t, s az eredmny is eljel nlkli. Az rtk az a legkisebb eljel nlkli egsz, amely kongruens az eljeles egsszel (modulo 2szmret). 2-es komplemens brzolsban a konverzi csupn elvi, a bitminta valjban nem vltozik. Ha az eljel nlkli egsz long-g alakul, az eredmny rtke szmszerleg ugyanaz, mint az eljel nlkli egsz. Igy a konverzi csupn a bal oldali kitlt nullk elhelyezsbl ll. 6.6. Aritmetikai konverzik Szmos opertor hasonl konverzit vlt ugyanabban a tpusban szolgltatja. Ezt aritmetikai konverzinak nevezni. Elszr is minden char vagy short tpus minden float operandus double-l alakul. ki, s az eredmnyt az eljrst szoksos operandus int-t s

Ezutn, ha valamelyik operandus double, akkor a msik is double-l alakul, s az eredmny szintn double lesz. Egybknt, ha valamelyik operandus long, a msik operandus s az eredmny tpusa is long lesz. Egybknt, ha valamelyik operandus unsigned, a msik is unsignedd alakul, s ez lesz az eredmny tpusa is.

10

Minden ms esetben mindkt operandusnak int-nek kell lennie lesz az eredmny tpusa is.

s ez

7. Kifejezsek A kifejezsekben elfordul opertorok precedencija ugyanaz, mint ebben a fejezetben az alfejezetek (pontok) sorrendje; a legmagasabb precedencia az els. gy pl. azokat a kifejezseket, amelyekre mint a + operandusaira hivatkozunk (7.4. pont) a 7. 1 . .. 7.3. pontokban definiljuk. Az egyes pontokon bell minden opertor azonos precedencij. Minden pontban megadjuk, hogy az ott trgyalt opertorokra bal-, ill. jobbirny asszociativits vonatkozik-e. A kifejezsekben alkalmazott opertorok precedencijt s asszociativitst a 18. pontban kzlt nyelvtan foglalja ssze. Egyb esetekben a kifejezsek kirtkelsnek sorrendje hatrozatlan. A fordtprogram a rszkifejezseket sajt megtlse szerint abban a sorrendben szmtja ki, amit leghatkonyabbnak vl, mg abban az esetben is, ha a rszkifejezseknek mellkhatsaik vannak. A mellkhatsok elfordulsnak sorrendje meghatrozott. Kommutatv s asszociatv opertorokat (*, +, &, |, n~) tartalmaz kifejezsek tetszs szerint rendezhetk mg zrjelek jelenltben is; ha adott sorrendben vgzend kirtkelst kvnunk elrni, explicit ideiglenes vltozt kell hasznlnunk. A kifejezsek kirtkelse sorn a tlcsorduls s az oszts ellenrzsnek kezelse gpfgg. A C nyelv minden ltez megvalstsa figyelmen kvl hagyja az egszek tlcsordulst; a 0val val oszts kezelse, ill. a lebegpontos kivtelek gprl gpre vltoznak, s ltalban valamilyen knyvtri fggvnnyel mdosthatk. 7.1. Elsdleges kifejezsek A . s -> szimblumokat, indexelst s fggvnyhvsokat tartalmaz elsdleges kifejezsek csoportostsa balrl jobbra trtnik. elsdleges_kifejezs: azonost lland karakterlnc (kifejezs) elsdleges_kifejezs [kifejezs] elsdleges_kifejezs [kifejezslistaopc] elsdleges_balrtk.azonost elsdleges_kifejezs->azonost Kifejezslista: kifejezs kifejezslista, kifejezs Az azonost elsdleges kifejezs, feltve, hogy az albbi ismertetett mdon helyesen deklarltk. Tpust a deklarcija hatrozza meg. Ha azonban az azonost tpusa valamilyen tmb, akkor az azonost kifejezs rtke a tmb els objektumt megcmz mutat, s a kifejezs tpusa a tmb alaptpusra hivatkoz mutat. A tmbazonost tovbb nem balrtk kifejezs. Hasonlkppen a fggvnyknt deklarlt azonost is a fggvny mutatjv alakul t, kivve, ha valamely fggvnyhvs fggvnynv-pozcijn fordul el. Az lland elsdleges kifejezs. Tpusa az alakjtl fggen lehet int, long vagy double. A karakterllandk tpusa int, a lebegpontos llandk double.

11

A karakterlnc elsdleges kifejezs. Tpusa eredetileg char-ok tmbje, de az azonostkra vonatkoz fenti szably rtelmben az a char-mutatv mdosul, s az eredmny a karakterlnc els karaktert megcmz mutat. (Kivtelt kpeznek egyes kezdetirtk-belltk (l. a 8.6. pontot.)) A zrjelezett kifejezs olyan elsdleges kifejezs, amelynek tpusa s rtke azonos a zrjel nlkli kifejezsvel. A zrjelek jelenlte nem befolysolja azt a tnyt, hogy a kifejezs balrtk-e vagy sem. Az elsdleges kifejezs s az azt kvet szgletes zrjelek kztti kifejezs szintn elsdleges kifejezst kpez [kifejezs]. Az elsdleges kifejezs ltalban valamilyen mutat tpus, az index kifejezs int, s az eredmny tpusa az a tpus, amelyre a mutat mutat. Az E1[E2] kifejezs definci szerint azonos a *((E1)+(E2))vel. Ez a pont, valamint az azonostkkal, a +-szal, ill. *-gal foglalkoz 7.1., 7.2., ill. 7.4. pont az sszes tudnivalt tartalmazza, ami ennek a jellsmdnak a megrtshez szksges. Az indexelsrl a 14.3. pontban szlunk. A fggvnyhvs olyan elsdleges kifejezs, amelyet zrjelek kztt a fggvny aktulis argumentumait alkot kifejezsek esetleg res, vesszkkel elvlasztott listja kvet. Az elsdleges kifejezsnek fggvny, amely visszaadja . . .-t tpusnak kell lennie, s a fggvnyhvs eredmnye . . . tpus. Mint a kvetkezkben ltni fogjuk, minden korbban el nem fordult azonost, amelyet kzvetlenl nyit zrjel kvet, a szvegkrnyezet alapjn egszt visszaad fggvnyknt deklarldik, gy a legkznsgesebb esetben az egsz rtk fggvnyeket nem kell deklarlni. A float tpus argumentumok hvs eltt double-l alakulnak t; minden char s short int-t konvertldik, s a tmbnevek, mint mindig, mutatkk alakulnak. Automatikusan semmilyen ms konverzi nem trtnik; lnyeges tudnunk, hogy a fordt az aktulis argumentumok tpust nem hasonltja ssze a formlis argumentumokval. Ha konverzira van szksg, hasznljunk tpusmdost szerkezetet (l. a 7.2. s 8.7. pontot). A fggvnyhvs elksztsekppen msolat kszl minden aktulis paramterrl, gy a C nyelvben minden argumentumtads szigoran rtk szerint trtnik. A fggvny megvltoztathatja formlis paramtereinek rtkt, de ezek a vltoztatsok nem befolysolhatjk az aktulis paramterek rtkt. Lehetsg van viszont mutat tadsra, tudva azt, hogy a fggvny megvltoztathatja annak az objektumnak az rtkt, amelyre a mutat mutat. A tmbnv mutatkifejezs. Az argumentumok kirtkelsnek sorrendjt a nyelv nem definilja; ne feledjk, hogy a klnbz fordtk eltrek! Brmilyen fggvny rekurzv mdon hvhat. Egy elsdleges kifejezs, az azt kvet pont s az azutn kvetkez azonost egyttesen kifejezst alkot. Az els kifejezsnek olyan balrtknek kell lennie, amely struktrt vagy uniont nevez meg, az azonost pedig meg kell, hogy nevezze a struktra vagy union egy tagjt. Az eredmny a struktra vagy union megnevezett tagjra vonatkoz balrtk. Egy elsdleges kifejezs, az azt kvet nyl (amelyet egy - s egy > alkot) s az azutn kvetkez azonost egyttesen kifejezst alkot. Az els kifejezsnek struktrt vagy uniont megcmz mutatnak kell lennie, s az azonostnak a struktra vagy union egy tagjt kell megneveznie. Az eredmny olyan balrtk, amely a mutatkifejezs ltal megcmzett struktra vagy union megnevezett tagjra vonatkozik.

12

gy az E1->MOS kifejezs azonos a (*E1).MOS kifejezssel. A struktrkkal s unionokkal a 8.5. pont foglalkozik. A hasznlatukra vonatkozan itt megadott szablyokat a fordt rugalmasan alkalmazza, hogy ki lehessen lpni a tpusmechanizmusbl (l. a 14.1. pontot). 7.2. Egyoperandus opertorok Az egyoperandus opertorokkal alkotott kifejezsek csoportostsa jobbrl balra trtnik. egyoper_kifejezs: *kifejezs &balrtk kifejezs !kifejezs ~kifejezs ++balrtk balrtk balrtk++ balrtk (tpusnv) kifejezs sizeof kifejezs sizeof (tpusnv) Az egyoperandus * opertor indirekcit fejez ki: a kifejezs mutat kellhogy legyen, s az eredmny olyan balrtk, amely a kifejezs ltal megcmzett objektumra vonatkozik. Ha a kifejezs mutat tpus, akkor az eredmny tpusa a mutatval megcmzett objektum tpusa. Az egyoperandus & opertor hatsra a balrtk ltal hivatkozott objektumot megcmz mutat keletkezik. Ha a balrtk tpusa . . ., akkor az eredmny tpusa mutat . . .-ra Az egyoperandus opertor az operandus negatv rtkt eredmnyezi. A szoksos aritmetikai konverzik mennek vgbe. Eljel nlkli (unsigned) mennyisg esetben a negatv rtket gy kell kiszmtani, hogy 2nbl levonjuk az operandus rtkt, (n az int-beli bitek szma). Egyoperandus + opertor nincs. A ! logikai neglopertor hatsra az eredmny 1 lesz, ha az operandus nulla, 0 lesz, ha az operandus nemnulla. Az eredmny tpusa int. Brmilyen aritmetikai tpusra s mutatkra alkalmazhat. A ~ opertor hatsra az operandus 1-es komplemense jn ltre. Megtrtnnek a szoksos aritmetikai konverzik. Az operandus integrlis tpus kell, hogy legyen. A ++ opertor balrtk operandusa eltt alkalmazva inkrementlja az operandus ltal hivatkozott objektumot. Az rtk az operandus j rtke, amely azonban nem balrtk. A ++x kifejezs x+=1-gyel egyenrtk. A konverzikra vonatkozan l. az sszeadsra (7.4. pont) s rtkad opertorokra (7.14. pont) vonatkoz ismertetst. Aopertor, ha balrtk operandusa eltt ll, az elbbiekhez hasonlan dekrementlja az operandust. Ha a ++ opertort valamely balrtk utn alkalmazzuk, az eredmny a balrtk ltal hivatkozott objektum rtke lesz. Az eredmny feljegyzse utn az objektum ugyangy inkrementldik, mint az ellrl alkalmazott ++ opertor esetben. Az eredmny tpusa ugyanaz, mint a balrtk kifejezs. Ha aopertort valamely balrtk utn alkalmazzuk, az eredmny a balrtk ltal hivatkozott objektum rtke lesz. Az eredmny feljegyzse utn az objektum ugyangy dekrementldik, mint az eltag opertor esetben. Az eredmny tpusa ugyanaz, mint a balrtk kifejezs.

13

Ha egy kifejezst valamelyik adattpus zrjelek kz rt neve elz meg, a kifejezs rtke a megadott tpusv alakul t. Ezt a konstrukcit tpusmdost szerkezetnek (cast) nevezzk. A tpusneveket a 8.7. pontban rjuk le. A sizeof opertor az operandusnak a byte-okban kifejezett mrett lltja el. (A byte-ot a nyelv csupn sizeof rtknek segtsgvel definilja. Azonban minden ltez megvalstsban a byte az a terlet, amely alkalmas egy char trolsra.) Tmbre alkalmazva az eredmny az sszes tmbbeli byte-ok szma lesz. A mretet a kifejezsben elfordul objektumok deklarcii hatrozzk meg. Ez a kifejezs szemantikailag egsz tpus lland, brhol hasznlhat, ahol llandra van szksg. Leginkbb olyan rutinokkal trtn kommunikci cljaira hasznlatos, mint pl. a trterlet-foglal fggvnyek s a be-s kivitel rendszerek. A sizeof opertor zrjelben ll tpusnvre is alkalmazhat. Ekkor egy, a megjellt tpus objektum mrett szolgltatja byte-okban. A sizeof(tpus) szerkezet sszefgg egysg, gy a sizeof(tpus)-2 kifejezs ugyanaz, mint (sizeof(tpus))-2. 7.3. Multiplikatv opertorok A * , / s % multiplikatv opertorok balrl jobbra csoportostanak. Megtrtnnek a szoksos aritmetikai konverzik. multiplikatv_kifejezs: kifejezs * kifejezs kifejezs / kifejezs kifejezs % kifejezs A ktoperandus * opertor a szorzst jelli. A * opertor asszociatv, s az ugyanazon a szinten tbb szorzst tartalmaz kifejezseket a fordt trendezheti. A ktoperandus / opertor az osztst jelli. Pozitv egszek osztsakor a csonkts nulla fel trtnik, de ha brmelyik operandus negatv, akkor a csonkts formja gpfgg. Az ebben a kziknyvben figyelembe vett gpek esetben az osztand s a maradk eljele megegyezik. Mindig igaz, hogy (a / b) * b + a % b megegyezik a-val (ha b nemnulla). A ktoperandus % opertor az els kifejezsnek a msodikkal trtn osztsbl szrmaz maradkot lltja el. A mvelet szoksos aritmetikai konverzikkal jr. Az operandusok nem lehetnek float tpusak. 7.4. Additv opertorok A + s - additv opertorok balrl jobbra csoportostanak. A szoksos aritmetikai konverzikat eredmnyezik. Mindkt opertor esetben vannak tovbbi tpuslehetsgek. additv_kifejezs: kifejezs + kifejezs kifejezs - kifejezs A + opertor alkalmazsnak eredmnye az operandusok sszege. Egy tmbbeli objektumot megcmz mutat s brmelyik integrlis tpus rtke sszeadhat. Az utbbi minden esetben relatv cmm alakul oly mdon, hogy megszorzdik annak az objektumnak a hosszsgval,

14

amelyre a mutat mutat. Az eredmny az eredetivel megegyez tpus mutat, amely ugyanannak a tmbnek egy msik elemre mutat, megfelel eltolssal az eredeti objektumhoz kpest. Ha teht P tmbelemet megcmz mutat, akkor a P+1 kifejezs a tmb kvetkez elemt megcmz mutat lesz. Mutatkra semmilyen ms tpus kombinci sem megengedett! A + opertor asszociatv, s az ugyanazon a szinten tbb sszeadst tartalmaz kifejezseket a fordt trendezheti. A opertor alkalmazsnak hatsra a kt operandus klnbsge keletkezik, a szoksos aritmetikai konverzik alkalmazsval. Ezenkvl mutatkbl le szabad vonni brmely integrlis tpus rtket, ekkor megtrtnnek ugyanazok a konverzik, mint az sszeadsnl. Ha kt ugyanolyan tpus objektumot megcmz mutatt vonunk ki egymsbl, az eredmny (az objektum hosszval trtn oszts rvn) int-t alakul, s a megcmzett objektumok kztt elhelyezked objektumok darabszmt adja meg. ltalnos esetben ez a konverzi vratlan eredmnyre vezet, kivve, ha a mutatk ugyanannak a tmbnek az elemeire mutatnak. Ennek az az oka, hogy mg az ugyanolyan tpus objektumok tvolsga sem felttlenl az objektumhosszsg tbbszrse. 7.5. Lptet opertorok A << s >> lptet (shift) opertorok balrl jobbra csoportostanak. Mindkett elvgzi az operandusokon a szoksos aritmetikai konverzikat; az operandusok mindegyike integrlis kell, hogy legyen. A mvelet sorn a jobb oldali operandus int-t alakul t; az eredmny tpusa megegyezik a bal oldali operandusval. Az eredmny hatrozatlan, ha a jobb oldali operandus negatv vagy nagyobb, mint az objektum bitekben mrt hosszsga, vagy pedig azzal megegyezik. lptet_kifejezs: kifejezs << kifejezs kifejezs >> kifejezs Az E1<<E2 rtke a bitmintaknt rtelmezett E1 E2 szm bittel balra lptetve; a kirlt bitek 0-val tltdnek fel. Az E1>>E2 rtke gy ll el, hogy E1 rtke E2 bittel balra lptetdik. A jobbra garantltan logikai jelleg (0-val trtn feltlts), ha az E1 unsigned; ms esetben aritmetikai lehet (s a PDP 11 -en az is lesz) ilyenkor a feltltds az eljelbittel trtnik. 7.6. Relcis opertorok A relcis opertorok balrl jobbra csoportostanak, de ez a tny nem klnsebben hasznos: a < b < cjelentse nem az, amit gondolnnk. relcis_kifejezs: kifejezs < kifejezs kifejezs > kifejezs kifejezs <= kifejezs kifejezs >= kifejezs A < (kisebb, mint), > (nagyobb, mint), <= (kisebb vagy egyenl) s >= (nagyobb vagy egyenl) opertorok mindegyike 0-t eredmnyez, ha a megadott relci rtke hamis, s 1 -et, ha igaz. Az eredmny tpusa int. A mveletek a szoksos aritmetikai konverzikkal jrnak. Kt mutat sszehasonlthat: az eredmny a megcmzett objektumok cmnek a cmtartomnyban val egymshoz kpesti elhelyezkedstl fgg. A mutat sszehasonlts csak akkor gpfggetlen, ha a mutatk ugyanabban a tmbben elhelyezked objektumokra mutatnak.

15

7.7. Egyenlsgi opertorok egyenlsg_kifejezs: kifejezs == kifejezs kifejezs != kifejezs A == (egyenl) s != (nem egyenl) opertorok pontosan ugyanolyanok, mint a relcis opertorok - csak a precedencijuk alacsonyabb. (Igy a < b == c < d rtke 1 , ha a < b s c < d igazsgrtke megegyezik.) Mutat s egsz sszehasonlthat, de az eredmny gpfgg, kivve ha az egsz a 0 lland. Az a mutat, amelyhez a 0-t rendeltnk hozz, garantltan nem mutat semmilyen objektumra, s 0val egyenlknt fog megjelenni; a hagyomnyos hasznlatban az ilyen mutatt nullnak tekintjk. 7.8. Bitenknti S opertor s_kifejezs: kifejezs & kifejezs Az & opertor asszociatv, s az &-et tartalmaz kifejezsek trendezhetk. A szoksos aritmetikai konverzik mennek vgbe; az eredmny az operandusok bitenknti S fggvnye. Az opertor csak integrlis operandusokra alkalmazhat! 7.9. Bitenknti kizr VAGY opertor kizr_vagy_kifejezs: kifejezs ^ kifejezs A ^ opertor asszociatv, s a ^-t tartalmaz kifejezsek trendezhetk. A mvelet a szoksos aritmetikai konverzikkal jr; az eredmny az operandusok bitenknti kizr VAGY fggvnye. Az opertor csak integrlis operandusokra alkalmazhat! 7.10. Bitenknti inkluzv VAGY opertor inkluzv vagy_kifejezs: kifejezs | kifejezs A | opertor asszociatv, s a |-ot tartalmaz kifejezsek trendezhetk. A mvelet a szoksos aritmetikai konverzikkal jr; az eredmny az operandusok bitenknti inkluzv VAGY fggvnye. Az opertor csak integrlis operandusokra alkalmazhat! 7.11. Logikai S opertor logikai s_kifejezs: kifejezs && kifejezs Az && opertor balrl jobbra csoportost. 1-et ad vissza; ha egyik operandusa sem nulla, egybknt 0-t. Az &-tl eltren az && biztostja a balrl jobbra trtn kirtkelst; ezen fell a msodik operandus nem rtkeldik ki, ha az els 0. Az operandusoknak nem kell azonos tpusaknak lennik, de mindegyikk tpusa vagy valamelyik alaptpus, vagy pedig mutat kell, hogy legyen. Az eredmny mindig int.

16

7.12. logikai VAGY opertor logikai vagy_kifejezs: kifejezs || kifejezs A || opertor balrl jobbra csoportost. 1_t ad vissza, ha valamelyik operandusa nemnulla, 0-t egybknt. A |-tl eltren a || biztostja a balrl jobbra trtn kirtkelst; ezen fell a msodik operandus nem rtkeldik ki, ha az els nemnulla. Az operandusoknak nem kell azonos tpusaknak lennik, de mindegyikk tpusa vagy valamelyik alaptpus, vagy pedig mutat kell, hogy legyen. Az eredmny mindig int. 7.13. A feltteles opertor feltteles_kifejezs: kifejezs ? kifejezs : kifejezs A feltteles kifejezsek balrl jobbra csoportostanak. Az els kifejezs kirtkeldik, s ha az rtke nemnulla, az eredmny a msodik kifejezs rtke lesz, egybknt pedig a harmadik kifejezs. Lehetsg szerint megtrtnnek a szoksos aritmetikai konverzik, amelyek rvn a msodik s a harmadik kifejezs azonos tpusv vlik; egybknt, ha mindkett ugyanolyan tpus mutat, az eredmny tpusa ez a kzs tpus lesz; vagy pedig az egyiknek mutatnak, a msiknak a 0 llandnak kell lennie, s az eredmny tpusa a mutat tpusa lesz. A msodik s a harmadik kifejezs kzl csak az egyik rtkeldik ki. 7.14. rtkad opertorok Tbb rtkad opertor van, amelyek mindegyike jobbrl balra csoportost. Bal oldali operandusknt mindegyikk egy-egy balrtket ignyel, az rtkad kifejezs tpusa a bal oldali operandus tpusval fog megegyezni. Az rtkad_ kifejezs rtke az az rtk lesz, amely az rtkads utn a bal oldali operandusban tallhat. Az sszetett rtkad opertor kt rsze klnll szintaktikai egysget kpez. rtkad_kifejezs: balrtk = kifejezs balrtk += kifejezs balrtk -= kifejezs balrtk *= kifejezs balrtk /= kifejezs balrtk %= kifejezs balrtk >>= kifejezs balrtk <<= kifejezs balrtk &= kifejezs balrtk ^= kifejezs balrtk |= kifejezs A legegyszerbb rtkadsnl, ahol az = opertort alkalmazzuk, a kifejezs rtke behelyettestdik a balrtk ltal hivatkozott objektum rtkbe. Ha mindkt operandus aritmetikai tpus, a jobb oldali operandus mg az rtkads eltt bal oldali tpusv alakul t. Az E1 op= E2 alak kifejezs hatst kikvetkeztethetjk, ha azt E1 = E2 op (E2) alaknak tekintjk; az E1 azonban csak egyszer rtkeldik ki. A += s -= esetben a bal oldali operandus mutat is

17

lehet, ekkor az (integrlis)jobb oldali operandus a 7.4. pontban mondottak szerint alakul t; minden jobb oldali operandus s az sszes nem-mutat jelleg bal oldali operandus aritmetikai tpus kell, hogy legyen. A jelenlegi fordtk megengedik mutat rtkl adst egsznek, egszt mutatnak, valamint mutatt ms tpus mutatnak. Az rtkads tisztnmsolsi mvelet, konverzi nlkl. Ez a fajta hasznlat gpfgg, s olyan mutatkat eredmnyezhet, amelyek hasznlatuk sorn cmzsi problmkhoz vezetnek. Annyi azonban bizonyos, hogy a 0 llandnak mutathoz val hozzrendelse olyan nulla-mutatt eredmnyez, amely brmilyen objektumot jell mutattl megklnbztethet. 7.15. A vessz opertor vessz_kifejezs: kifejezs , kifejezs A vesszvel elvlasztott kifejezspr balrl jobbra rtkeldik ki, s a bal oldali kifejezs rtke megegyezik a jobb oldali operandus tpusval s rtkvel. Ez az opertor balrl jobbra csoportost. Olyan szvegkrnyezetben, ahol a vessznek specilis jelentse van, pl. fggvnyek aktulis argumentumainak listjban (7.1. pont) s a kezdeti rtkek listjban (8.6. pont), az itt ismertetett vessz opertor csak zrjelek kztt jelenhet meg; pl. f (a, (t = 3 , t + 2), c) nek hrom argumentuma van; ezek kzl a msodiknak az rtke 5. 8. Deklarcik A deklarcik segtsgvel hatrozzuk meg, hogyan rtelmezze a C fordt az egyes azonostkat; a deklarcik nem felttlenl jelentenek trterlet-foglalst az azonost szmra. A deklarcik alakja: deklarci: dekl._specifiktorok deklartorlistaopc; A deklartorlistban elhelyezked deklartorok a deklarland azonostkat tartalmazzk. A deklarcispecifiktorok tpus- s trolsiosztly-meghatrozsok sorozatbl llnak. dekl._specifiktorok: tpusspecifiktor dekl._specifiktorokopc t.o._specifiktor dekl._specifiktorokopc A listt az albbiak szerint kvetkezetesen kell megszerkeszteni. 8.1. Trolsiosztly-specifiktorok specifiktorok az albbiak: t.o._specifiktor: auto static extern register typedef A trolsiosztly-

18

A typedef specifiktor nem foglal trhelyet, s csak a szintaktikai knyelem kedvrt nevezzk trolsiosztly-specifiktornak (l. a 8.8. pontot). A klnfle trolsi osztlyok jelentst a 4. pontban ismertettk. Az auto, static s register deklarcik definciknt is szolglnak, amennyiben megfelel nagysg trterlet lefoglalst is elidzik. Az extern esetben a megadott azonostk kls defincijnak (10. pont) is szerepelnie kell valahol azon a fggvnyen kvl, amelyben deklarltuk ket. A register deklarcit legclszerbb olyan auto deklarcinak tekinteni, amely mg azt is jelzi a fordtnak, hogy a deklarlt vltozkat srn fogjuk hasznlni. Csupn az els nhny ilyen deklarcinak lesz hatsa. Ezenkvl csupn nhny tpus troldik tnylegesen regiszterekben; a PDP- 11 -en ezek a tpusok az int, a char s a mutat. Mg egy megszorts vonatkozik a regiszter tpus vltozkra: nem alkalmazhat rjuk az & (cme valaminek) opertor. A regiszterdeklarcik megfelel hasznlatval kisebb mret, gyorsabb programokhoz juthatunk, a kdgenerls tovbbfejlesztsvel azonban lehet, hogy alkalmazsuk feleslegess vlik. Egy deklarciban legfeljebb egy t. o. -specifiktort lehet megadni. Ha a t.o._specifiktor hinyzik a deklarcibl, akkor azt a fordt fggvnyen bell auto-nak, fggvnyen kvl extern-nek tekinti. Kivtel: a fggvnyek sohasem automatikusak! 8.2. Tpus-specifiktorok A tpus-specifiktorok az albbiak : tpus-specifiktor: char short int long unsigned float double strukt._vagy_union_specifiktor typedef_nv A long (hossz), short (rvid) s unsigned (eljel nlkli) szavakat jelzknek tekinthetjk; az albbi kombincik fogadhatk el: short int long int unsigned int long float Az utbbi ugyanazt jelenti, mint a double. Egybknt egy deklarcin bell legfeljebb egy tpus-specifiktor adhat meg. Ha a deklarcibl hinyzik a tpus-specifiktor, akkor a deklarlt vltozt a fordt int-nek tekinti. Struktrk s unionok specifiktoraival a 8.5. foglalkozik; a typedef nevekkel trtn deklarcikat a 8.8. trgyalja. pont pont

8.3. Deklartorok A deklarciban megjelen deklartorlista deklartorok vesszkkel elvlasztott sorozata, amelyek mindegyike kezdeti rtkkel (k..) rendelkezhet. deklartorlista: k.._deklartor k.._deklartor , deklartorlista

19

k.._deklartor: deklartor inicializlopc A kezdeti rtkekkel a 6.6. pont foglalkozik. A deklarcibeli specifiktorok megadjk azoknak az objektumoknak a tpust s trolsi osztlyt, amelyekre a deklartorok vonatkoznak. A deklartorok szintaxisa: deklartor: azonost (deklartor) *deklartor deklartor () deklartor [lland_kifejezsopc] A csoportosts ugyanolyan, mint a kifejezsekben. 8.4. A deklartorokjelentse Minden deklartort arra vonatkoz lltsnak tekinthetnk, hogy ha valamely kifejezsben a deklartorral megegyez alak szerkezet jelenik meg, akkor az a megjellt tpus s trolsi osztly objektumot fogja eredmnyezni. Minden deklartor pontosan egy azonostt tartalmaz, ez az azonost az, amelyet deklarlunk. Ha deklartorknt bvtmny nlkli azonost szerepel, akkor annak tpusa az lesz, amit a deklarcit bevezet specifiktor megjell. A zrjelek kztti deklartor azonos a zrjel nlklivel, de az sszetett deklartorok ktsi sorrendje zrjelekkel megvltoztathat (l. a kvetkez pldkat). Most kpzeljk el a T D1 deklarcit, ahol T a tpus-specifiktor (mint az int stb.) s D1 a deklartor. Tegyk fel, hogy e deklarci hatsra az azonost tpusa . . .T lesz, ahol . . res, ha D1 csupn sima azonost (teht x tpusa int x-ben egyszeren int). Ha viszont D1 alakja *D akkor az ltala tartalmazott azonost tpusa . . .mutat T-re. Ha D1 alakja D () akkor az ltala tartalmazott azonost tpusa . . . fggvny, amely T-t ad vissza. Ha D1 D [lland_kifejezs] vagy D [] alak, akkor az ltala tartalmazott azonost tpusa T . . . tmbje. Az els esetben az lland kifejezs olyan kifejezs, amelynek rtke fordtsi idben meghatrozhat s amelynek tpusa int Az lland kifejezsek pontos defincija a 15. pontban tallhat.) Ha tbb . . .tmbje specifikci egymssal szomszdos, akkor tbbdimenzis tmb keletkezik; a tmbhatrokat rgzt lland

20

kifejezsek csupn a sorozat els tagjnl hinyozhatnak. Ez az elhagys akkor hasznos, ha kls tmbrl _ van sz, s a trfoglalst elidz definci mshol szerepel. Az els lland kifejezs akkor is elhagyhat, ha a deklartort kezdeti rtk kveti. Ilyenkor a fordt a mretet a megadott kezdeti rtkek szmbl szmtja ki. Tmbt az alaptpusok valamelyikbl, mutatkbl, struktrkbl, unionokbl vagy ms tmbkbl (tbbdimenzis tmbt generlva) alkothatunk. A fenti szintaxissal definilt lehetsgek kzl nem mindegyik megengedett. A megszortsok a kvetkezk : fggvnyek nem adhatnak vissza tmbket, struktrkat, unionokat vagy fggvnyeket, de visszaadhatnak ilyeneket megcmz mutatkat; fggvnyekbl nem kpezhet tmb, de ltezik fggvnyeket megcmz mutatkbl kpzett tmb. Hasonlkppen, a struktrk s unionok sem tartalmazhatnak fggvnyt, legfeljebb fggvnyt megcmz mutatt. Pldul int i, *ip, f (), *fip (), (*pfi) () deklarlja az i egszt, az ip egszt megcmz mutatt, az egszt visszaad f fggvnyt, az egszt megcmz mutatt visszaad fip fggvnyt s a pfi mutatt, amely egy egszt visszaad fggvnyre mutat. Klnsen hasznos ha a kt utolst hasonltjuk ssze. A *fip() ktsi sorrendje *(fip()), gy a deklarci azt rja el, ill. egy kifejezsben elfordul ilyen szerkezet azt vltja ki, hogy a fip fggvny meghvsa utn a (mutatjelleg) eredmnyen keresztli indirekcival egy egsz lljon el. A (*pfi)() deklartorban (vagy a szerkezetet felhasznl kifejezsekben) a plusz zrjelek szksgesek: azt jelzik, hogy a fggvnyt megcmz mutatn keresztli indirekci fggvnyt eredmnyez, amely meghvsa utn egszt ad vissza. Msik pldaknt float fa [17], *afp [17]; egy float szmokbl ll tmbt s egy float szmokat megcmz mutatkbl ll tmbt deklarl. Vgezetl static int x3d [3)[5][7]; egszek statikus, hromdimenzis tmbjt deklarlja, amelynek mrete 3 * 5 * 7. Rszleteiben nzve x3d hromelem tmb; minden elem t tmbt tartalmaz; az utbbiak mindegyike 7 darab egszbl ll. Az x3d, x3d[i], x3d[i][j], x3d[i][j] [k] alakok brmelyike elfordulhat valamely kifejezsben. Az els hrom tmb tpus, az utols tpusa int. 8.5. Struktra- s union deklarcik A struktra nvvel elltott tagok sorozatt tartalmaz objektum. Minden tag tetszleges tpus lehet. Az union olyan objektum, amely adott idpillanatban tbb lehetsges tag brmelyikt tartalmazhatja. A struktra- s az unionspecifiktorok azonos alakak. strukt._vagy_union_specifiktor: strukt._vagy_union { strukt._dekl._lista} strukt._vagy_union azonost {strukt._dekl._lista} strukt._vagy_union strukt._vagy_union: struct union azonost

21

A struktradeklartor-lista a struktra vagy union tagjaira vonatkoz deklarcik felsorolsa: strukt._dekl._lista: strukt._deklarci strukt._deklarci strukt._dekl._lista strukt._deklarci: tpus_specifiktor strukt._deklartor_lista: strukt._deklartor strukt._deklartor , strukt._deklartor_lista Kznsges esetben a strukt. deklartor egyszeren a struktra vagy union valamely tagjnak deklartora. A struktra tagjai adott szm bitet is tartalmazhatnak. Az ilyen tag neve mez (field), hosszt a nvtl kettspont vlasztja el. strukt. _deklartor: deklartor deklartor : lland_kifejezs : lland_kifejezs A struktrn bell a deklarlt objektumok cmei a deklarcikban balrl jobbra haladva nvekednek. A struktra minden nem-mez tagja a tpusnak megfelel cmhatron kezddik, gy a struktrban nv nlkli lyukak helyezkedhetnek el. A mez jelleg tagok gpi egszekben helyezkednek el, szhatrokon nem nylnak t. Az a mez, amely nem fr el egy szban mg fennmaradt helyen, a kvetkez szba kerl. A mez nem lehet szlesebb, mint a sz. Mezk hozzrendelse PDP-11-en jobbrl balra, ms gpeken balrljobbra trtnik. A deklartor nlkli, csupn kettspontot s a szlessget tartalmaz struktradeklartor olyan nv nlkli mezt jell ki, amelyet kvlrl elrt elrendezseknek megfelel kitltsre hasznlhatunk. Specilis esetben a 0 szlessg nv nlkli mez a kvetkez mez szhatrra trtn illesztst rja el. A kvetkez mez felteheten tnyleg mez, nem pedig kznsges struktratag, mivel az utbbi esetben ez az illeszts automatikusan megtrtnne. A nyelv nem r el korltozst a mezknt deklarlt objektumok tpusra vonatkozlag, a megvalstsoktl azonban nem vrjuk el, csak az egsz tpus mezk tmogatst. St, mg az int mezket is eljel nlklinek tekinthetik. A PDP-11-en a mezknek nincs eljelk, s csak egsz rtkek lehetnek. Egyetlen megvalstsban sincsenek mezkbl kpzett tmbk, tovbb a mezkre az & cmopertor sem alkalmazhat, vagyis nincsenek mezket megcmz mutatk sem. Az uniont olyan struktrnak kpzelhetjk, amelynek tagjai a 0 relatv cmen kezddnek, s amelynek mrete elegenden nagy ahhoz, hogy brmelyik tagjt tartalmazhassa. Az union egyszerre legfeljebb egy tagjt tartalmazhatja. A msodik alak struktra- vagy unionspecifiktor, vagyis a struct azonost {strukt._dekl._lista} union azonost {strukt._dekl._lista} egyike, az azonostt a lista ltal meghatrozott struktra struktracmkjeknt (vagy unioncmkjeknt) deklarlja. Az ezt kvet deklarcikban azutn a specifiktor harmadik alakja, a strukt._deklartor_lista

22

struct azonost union azonost alakok egyike hasznlhat. A struktracmkk lehetv teszik nhivatkoz struktrk definilst; megengedik, hogy a deklarci hossz rszt csupn egyszer adjuk meg s tbb alkalommal hasznljuk. Tilos olyan struktrt vagy uniont deklarlni, amelyben sajt maga elfordul, de a struktra vagy union tartalmazhat sajt magt megcmz mutatt! A tagok s cmkk nevei megegyezhetnek a kznsges vltozk neveivel. A cmkk s a tagok nevnek azonban egymstl el kell trnik! Kt struktrnak lehet kzs kezdeti tagsorozata, azaz ugyanaz a tag kt klnbz struktrban is megjelenhet, ha mindkettben azonos a tpusa s ha az sszes megelz tag is mind a kettben azonos. (A fordt tulajdonkppen csak azt ellenrzi, hogy a kt klnbz struktrban elfordul nv tpusa s relatv cme megegyezik-e, de ha a megelz tagok klnbznek, akkor a szerkezet nem gpfggetlen.) A struktradeklarci egyszer pldja: struct tnode { char tword [20]; int count; struct tnode * left; struct tnode *right; }; amely 20 karakterbl ll tmbt, egy egszt s kt, hasonl struktrt megcmz mutatt tartalmaz. E deklarci megadsa utn a struct tnode s, *sp; deklarci szerint s a megadott jelleg struktra lesz, s sp az ilyen jelleg struktrt megcmz mutat. Ezeknek a deklarciknak az alapjn az sp->count kifejezs annak a struktrnak a count nev mezjre mutat, amelyre az sp utal; s.left az s struktra bal oldali rszfjnak mutatjra vonatkozik, mg s.right->tword [0] az s struktra jobb oldali rszfja tword nev tagjnak els karakterre utal. 8.6. Inicializls A deklartor megadhatja a deklarlt azonost kezdeti rtkt. Az inicializlt = elzi meg, s kapcsos zrjelek kz zrt kifejezst vagy rtklistt tartalmaz. inicializl: = kifejezs = { inicializl_lista } = { inicializl_lista ,} inicializl_lista: kifejezs inicializl_lista , inicializl_lista ( inicializl_lista )

23

A statikus vagy kls vltozk inicializliban kizrlag lland kifejezsek (l. a 15. pontot), vagy pedig olyan kifejezsek szerepelhetnek, amelyek valamelyik korbban deklarlt vltoz cmre reduklhatk (az albbitl egy lland kifejezssel val cmeltols is lehetsges). Az automatikus s regisztervltozk esetben tetszleges inicializls lehetsges llandk, korbban deklarlt vltozk s fggvnyek bevonsval. Inicializlatlan statikus s kls vltozk kezdeti rtke garantltan nulla; az inicializlatlan automatikus s regisztervltozkban pedig indulskor biztos hulladk van. Ha az inicializlt skalr mennyisgre (mutatra vagy aritmetikai tpus objektumra) alkalmazzuk, tartalma egyetlen, esetleg kapcsos zrjelek kztti kifejezs. Az objektum kezdeti rtkt a gp a kifejezs alapjn szmtja ki; a konverzik ugyanazok, mint rtkadsnl. Ha a deklarlt vltoz aggregtum (struktra vagy tmb jelleg sszetett mennyisg), akkor az inicializl az aggregtum tagjainak kapcsos zrjelek kztti, vesszkkel elvlasztott listjt tartalmazza. Az inicializlkat az indexek vagy tagok nvekv sorrendjben adjuk meg. Ha az aggregtum rszaggregtumokat tartalmaz, ugyanez a szably vonatkozik rekurzv mdon az aggregtum tagjaira. Ha a listban kevesebb inicializl van, mint ahny tagja van az aggregtumnak, akkor az aggregtum nullkkal tltdik ki. Unionok s automatikus aggregtumok inicializlsa nem megengedett! A kapcsos zrjeleket a kvetkez mdon hagyhatjuk el. Ha az inicializl bal oldali kapcsos zrjellel kezddik, akkor a rkvetkez, vesszkkel elvlasztott inicializllista az aggregtum tagjait inicializlja; Ha, ha itt tbb inicializl van, mint tag. Ha azonban az inicializl nem bal oldali kapcsos zrjellel kezddik, akkor a fordtprogram a listbl csupn az aggregtum tagjainak megfelel szm elemet vesz figyelembe; a listban fennmarad tagok annak az aggregtumnak a kvetkez elemt fogjk inicializlni, amelynek a szban forg aggregtum a rsze. Vgl megemltjk, hogy a char tmbk rviden, karakterlncokkal inicializlhatk. Ez esetben a lnc egymst kvet karakterei a tmb egyes elemeit inicializljk. Inicializlsi pldk: int x [] = {1,3,5}; az x-et olyan egydimenzis tmbknt deklarlja s inicializlja, amelynek hrom eleme van, mivel mretet nem adtunk meg s hrom inicializl van. float y [4][3] ={ {1, 3, 5}, {2, 4, 6}, {3, 5, 7}, }; teljes zrjelezett inicializls: 1 , 3 s 5 az y[0] tmb els sort, mgpedig az y[0][0], y[0][1] s y[0][2] elemeket inicializljk. A kvetkez kt sor hasonl mdon inicializlja y[1]-et s y[2]-t. Az inicializl tl hamar r vget, s ezrt y[3] 0-val inicializldik. Pontosan ugyanezt az eredmnyt rtk volna el float y [4][3] ={

24

1, 3, 5, 2, 4, 6, 3, 5, 7 }; megadsval. y inicializlja bal oldali kapcsos zrjellel kezddik, de y[0]- nem, gy a gp a listbl hrom elemet hasznl fel. Hasonlkppen a kvetkez hrom y[1 ]-, az azt kvet hrom pedig y[2]- lesz. Ugyangy, ; float y [4][3] ={ {1}, {2}, {3}, {4} }; a (ktdimenzis tmbnek tekintett) y els oszlopt inicializlja s a tbbi elemet 0 rtknek hagyja meg. Vgezetl char msg [] = Szintaktikai hiba a %s-edik sorban \n; olyan karaktertmbt mutat, amelynek karakterlnccal inicializltuk. elemeit

8.7. Tpusnevek Kt sszefggsben (tpusmdost szerkezettel vgzett explicit tpuskonverzi esetn s a sizeof argumentumaknt) kell valamilyen adattpus nevt megadnunk. Ez tpusnv hasznlatval trtnik, ami lnyegben egy adott tpus objektum olyan deklarcija, amelybl hinyzik az objektum neve. tpus_nv: tpus_specifiktor absztrakt_deklartor absztrakt_deklartor: res ( absztrakt_deklartor ) *absztrakt_deklartor absztrakt_deklartor () absztrakt_deklartor [lland_kifejezsopc] A ktrtelmsg elkerlse rdekben az ( absztrakt_deklartor ) szerkezetben az absztraktdeklartor nem lehet res. E megszorts figyelembevtelvel egyrtelmen azonosthat az absztrakt-deklartorban az a hely, ahol az azonost megjelenne, ha a szerkezet egy deklarcin belli deklartor lenne. A megnevezett tpus ekkor ugyanaz lesz, mint a hipotetikus azonost tpusa. Pl. int int * int *[3] int (*) [3] int * () int (*) () sorban megnevezi az egsz, egszt megcmz mutat, 3 darab egszmutatbl ll tmb, 3 egszbl ll tmbt megcmz mutat, egszt megcmz mutatt visszaad fggvny s az egszt visszaad fggvnyt megcmz mutattpusokat. 8.8. Typedef Az olyan deklarcik, amelyeknek a trolsi osztlya typedef, nem trterletet definilnak, hanem olyan azonostkat, amelyeket a

25

ksbbiekben gy hasznlhatunk, mintha az alapvet leszrmaztatott tpusokat megnevez kulcsszavak lennnek: typedef_nv: azonost

vagy

A typedef-et tartalmaz deklarci rvnyessgi tartomnyn bell minden ott elfordul deklartor rszeknt megjelen azonost szintaktikusan egyenrtk lesz azzal a tpuskulcsszval, amely a 8.4. pontban lert mdon megnevezi az azonosthoz trstott tpust. Pl. typedef int im;}complex; utn a MILES distance; extern KLICKSP metricp; complex z, *zp; szerkezetek mindegyike megengedett deklarci; a distance tpusa int, a metricp- int-et megcmz mutat, a z- pedig a megadott struktra. zp az ilyen struktrt megcmz mutat. A typedef nem teljesen j tpusokat vezet be, csupn ms mdon is megadhat tpusok szinonimit. Igy a fenti pldban distance pontosan ugyanolyan tpus, mint minden ms int objektum. 9. Utastsok Az utastsok egymst kveten, sorban hajtdnak vgre, az ettl val eltrst kln jelezzk. 9.1. A kifejezs utasts jelleg; ezek alakja: kifejezs; A kifejezs jelleg fggvnyhvsok. utastsok A legtbb utasts kifejezs MILES, *KLICKSP; typedef struct { double re,

legtbbszr

rtkadsok

vagy

9.2. Az sszetett utasts vagy blokk Annak rdekben, hogy ott, ahol elvileg csak egy utasts helyezhet el, tbb utasts is hasznlhat legyen, rendelkezsre ll az sszetett utasts (ms szval blokk). sszetett_utasts: { deklarcilistaopc utastslistaopc} deklarcilista: deklarci deklarci deklarcilista utastslista: utasts utasts utastslista Ha a deklarcilistban elfordul brmelyik azonostt mr korbban deklarltuk, a kls deklarci a blokk vgrehajtsnak idtartamra rvnyt veszti, majd annak befejeztvel visszanyeri hatlyt. Az auto s register vltozk brmilyen inicializlsa minden alkalommal jra megtrtnik, amikor a vezrls a blokkba fellrl belp. Jelenleg lehetsges (de helytelen gyakorlat) a blokk belsejbe val ugrats; ez esetben az inicializlsok elmaradnak. A static vltozk kezdeti rtknek belltsa csupn egyszer, a program vgrehajtsnak kezdetn trtnik meg. A blokkon bell az extern

26

deklarcik hatsra nincs trfoglals, gy ezek inicializlsa nem megengedett. 9.3. A feltteles utasts if (kifejezs) utasts if (kifejezs) utasts else utasts A gp mindkt esetben kirtkeli a kifejezst, s ha rtke nemnulla, az els alutastst hajtja vgre. A msodik esetben, ha a kifejezs rtke 0, a msodik alutastst hajtja vgre. Az else-vel kapcsolatos szoksos ktrtelmsget a C gy oldja fel, hogy az else az utoljra tallt else nlkli if-hez ktdik. 9.4. A while utasts A while utasts alakja: while (kifejezs) utasts Az alutasts vgrehajtsa mindaddig ismtldik, amg a kifejezs rtke nemnulla marad. A vizsglat mindig az utasts egyes vgrehajtsai eltt trtnik. 9.5. A do utasts A do utasts alakja do utasts while (kifejezs); Az alutasts vgrehajtsa mindaddig ismtldik, amg kifejezs rtke nullv nem vlik. A vizsglat mindig az utasts egyes vgrehajtsai utn trtnik. 9.6. A for utasts A for utasts alakja: for (1._kifejezsopc; 2._kifejezsopc; 3._kifejezsopc) utasts Ez az utasts egyenrtk az 1._kifejezs; while (2._kifejezs) { utasts 3._kifejezs; } alakkal. Eszerint az els kifejezs a ciklust inicializlja; a msodik azt a vizsglatot hatrozza meg, amely minden itercit megelz, s a vezrls kilp a ciklusbl, ha a kifejezs nullv vlik; a harmadik kifejezs gyakran az egyes itercik utn vgrehajtand inkrementlst hatrozza meg. A kifejezsek brmelyike, vagy akr mindegyik elhagyhat. Ha a 2. kifejezs hinyzik, akkor a megfelel while utastsbl while( 1 ) lesz; a tbbi hinyz kifejezs egyszeren elmarad az elbbi kifejtett formbl. 9.7. A switch utasts A switch utasts hatsra a megadott kifejezs rtktl fggen a vezrls tbb utasts valamelyikre addik t.

27

Alakja: switch (kifejezs) utasts A kifejezsben megtrtnnek a szoksos aritmetikai konverzik, de az eredmnynek int-nek kell lennie. Az utasts ltalban sszetett. A switch utastson bell elfordul brmelyik utasts megcmkzhet egy vagy tbb case eltaggal az albbi mdon : case lland_kifejezs: ahol az lland kifejezs int kell, hogy legyen. Ugyanazon a switch-en bell kt case llandnak nem lehet egyforma rtke. Az lland kifejezsek pontos defincijt a 15. pont tartalmazza. Legfeljebb egy darab default: alak utasts-eltag is elfordulhat a switch utastsban. A switch utasts vgrehajtsa sorn a gp kirtkeli a benne elfordul kifejezst s sszehasonltja minden egyes case llandval. Ha a case llandk valamelyike megegyezik a kifejezs rtkvel, a vezrls az illeszked case eltagot kvet utastsra addik t. Ha egyik lland sem egyezik meg a kifejezs rtkvel, s szerepel a default eltag, akkor a program vgrehajtsa az ezt kvet utastson folytatdik. Ha egyik case sem illeszkedik s nincs default, akkor a gp a switch-ben elfordul utastsok kzl egyiket sem hajtja vgre. A case s default eltagok egymagukban nem vltoztatjk meg a vezrls menett, amely zavartalanul vgighalad ezeken az eltagokon. A switchbl val kilpsre vonatkozlag l. a break utastst a 9.8. pontban. A switch trgyt kpez utasts legtbbszr sszetett. Deklarcik szerepelhetnek ennek az utastsnak a fejben, de az automatikus s regisztervltozk inicializlsai hatstalanok. 9.8. A break; utasts hatsra befejezdik a break-et krlvev legbels while, do, for vagy switch utasts vgrehajtsa; a vezrls a befejezett utastst kvet utastsra addik t. 9.9. A continue; utasts hatsra a vezrls a continue-t krlvev legbels while, do vagy for utasts while (...) { ... contin: ; do { ciklusfolytat for (...) { rszre addik t, vagyis a ciklus vgre. Pontosabban, a ... ... contin: ; contin: ; } } while (...); } utastsok mindegyikben a continue utasts egyenrtk a goto contin-nel. A contin: utn nulla utasts szerepel, (l. a 9.13. pontot). A continue utasts A break utasts

28

9.10. A return utasts A fggvny a hvjhoz a return utasts segtsgvel tr vissza, amelynek lehetsges alakja: return ; return kifejezs; Az els esetben a visszaadott rtk hatrozatlan. A msodik esetben a kifejezs rtke kerl vissza a fggvny hvjhoz. Szksg esetn az rtkadshoz hasonlan a kifejezs olyan tpusv alakul t, mint amilyen tpus fggvnyben elfordul. A fggvny vgnek tlpse azonos a visszatrsi rtk nlkli return-nel. 9.11. A goto utasts A vezrls felttel nlkl a goto azonost; utasts segtsgvel adhat t. Az azonost az ppen vgrehajtott fggvnyen bell elhelyezett cmke (l. a 9.12. pontot) kell, hogy legyen. 9.12. A cmkzett utasts Brmelyik utastst megelzhetik az azonost: alak eltagok, amelyek az azonostt cmkeknt deklarljk. A cmke egyedl a goto clpontjaknt szolgl. A cmke rvnyessgi tartomnya az a fggvny, amelyben elfordul, kivve azokat az alblokkokat, amelyekben ugyanezt az azonostt jradeklarltk (l. a 11. pontot). 9.13. A nulla utasts A nulla utasts alakja: ; A nulla utasts hordozhat pl. cmkt kzvetlenl valamely sszetett utasts }-e eltt, vagy pedig a while-hoz hasonl valamelyik ciklusutasts szmra res ciklustrzset kpezhet. 10. Kls defincik A C program kls (external) defincik sorozatt tartalmazza. A kls definci a vltozt extern (ez az alaprtelmezs) vagy static trolsi osztlynak s megadott tpusnak deklarlja. A tpusspecifiktor (l. a 8.2. pontot) lehet res, ebben az esetben a tpust int-nek tekintjk. A kls defincik rvnyessgi tartomnya annak az llomnynak a vgig tart, amelyben deklarltk ket; hasonlkppen a deklarcik is az llomny vgig rvnyesek. A kls defincik szintaxisa ugyanaz, mint az sszes deklarci, azzal a klnbsggel, hogy a fggvnyeket csak ezen a szinten lehet definilni. 10.1. Kls fggvnydefincik A fggvnydefincik alakja: fggvnydefinci: dekl._specifiktorokopc fggvny deklartor fggvnytrzs A deklarcispecifiktorok kzl trolsiosztly-specifiktorknt csupn az extern s a static megengedett; a kett kztti klnbsgre nzve l. a 11.2. pontot. A fggvnydeklartor hasonl a fggvny, amely . . .-t ad vissza jelleg deklartorhoz, azzal a klnbsggel, hogy megadja a definilt fggvny formlis paramtereinek listjt. fggvnydeklartor:

29

deklartor (paramterlistaopc) paramterlista: azonost azonost , paramterlista A fggvnytrzs alakja: fggvnytrzs: deklarcilista sszetett_utasts A paramterlistabeli azonostk s csakis ezek deklarlhatk a deklarcilistban. Az olyan azonostt, amelynek tpust nem adtuk meg, a fordts int-nek tekinti. Az egyetlen megadhat trolsi osztly a register; ha ez szerepel, akkor a neki megfelel aktulis paramter, amennyiben lehetsges, a fggvny vgrehajtsnak kezdetn valamelyik regiszterbe kerl. Egyszer plda a teljes fggvnydefincira: int max (a, b, c) int a, b, c; { int m; m = (a > b) ? a : b; return ((m > c) ? m : c); } Itt az int a tpus-specifiktor; max(a, b, c) a fggvnydeklartor; int a, b, c; a formlis paramterek deklarciinak listja; { . . } az utasts programkdjt megad blokk. A C az sszes float tpus aktulis paramtert double-l alaktja t, gy a float-nak deklarlt formlis paramterek deklarcii is doublel mdosulnak. Tovbb, mivel a tmbre trtn hivatkozs brmilyen sszefggsben klnsen aktulis paramterknt) olyan mutatt jelent, amely a tmb els elemre mutat, a . . . tmbje alakban deklarlt formlis paramterek deklarcii mutat . . .-ra alakra mdosulnak. Vgezetl, mivel a struktrk, unionok s fggvnyek nem adhatk t fggvnynek, rtelmetlen dolog formlis paramtereket struktrnak, unionnak vagy fggvnynek deklarlni (az ilyen objektumokat megcmz mutatk termszetesen megengedettek). 10.2. Kls adatdefincik A kls adatdefincik alakja: adatdefinci: deklarci Az ilyen adatok trolsi osztlya lehet extern (ez az alaprtelmezs) vagy static, de nem lehet auto, sem pedig register. 11. Az rvnyessgi tartomny szablyai Nem szksges az egsz C programot egyszerre fordtani: a program forrsszvege tbb llomnyban trolhat, s knyvtrakbl elre lefordtott rutinokat lehet betlteni. A program fggvnyei kztti kommunikci akr explicit hvsokkal, akr kls adatokon keresztl megvalsthat. Ennek kvetkeztben ktfle rvnyessgi tartomnyrl kell beszlnnk: elszr is arrl, amit az azonost lexiklis rvnyessgi tartomnynak neveznk, s ami lnyegben a programnak az a rsze, amelyben a definilatlan azonost (undefined identifier) hibazenet elfordulsa nlkl hasznlhatjuk, msodszor pedig a kls azonostkhoz tartoz rvnyessgi tartomnyrl, amelyre az a szably jellemz, hogy az ugyanarra a kls azonostra

30

vonatkoz hivatkozsok ugyanarra az objektumokra val hivatkozsokat jelentenek. 11.1. Lexiklis rvnyessgi tartomny A kls defincikban deklarlt azonostk lexiklis rvnyessgi tartomnya a definciktl az ket tartalmaz forrsllomny vgig tart. A formlis paramterknt elfordul azonostk rvnyessgi tartomnya az a fggvny, amelyhez tartoznak. A blokkok fejben deklarlt azonostk rvnyessgi tartomnya a blokk vgig terjed. A cmkk rvnyessgi tartomnya az egsz fggvny,amelyben elfordulnak. Mivel az ugyanarra a kls azonostra utal sszes hivatkozs ugyanarra az objektumra vonatkozik (l. a 11.2. pontot), a fordtprogram ellenrzi, hogy ugyanannak a kls azonostnak az sszes deklarcija kompatibilis-e; valjban ezek rvnyessgi tartomnya kiterjed az egsz llomnyra, amelyben elfordulnak. Minden esetben fennll azonban, hogy ha egy azonost explicit mdon egy blokk - akr fggvnyt alkot blokk - fejben deklarlunk, akkor annak vgig az illet azonost sszes, a blokkon kvl elfordul deklarcija felfggesztdik. Emlkezznk arra is (l. a 8.5. pontot), hogy egyrszt a kznsges vltozkhoz, msrszt a struktra-, ill. uniontagokhoz s cmkkhez kapcsold vltozk kt kln osztlyt alkotnak, amelyek kztt nincs tkzs. A tagokra s cmkkre ugyanazok az rvnyessgi tartomny szablyok vonatkoznak, mint a tbbi azonostkra. A typedef nevek ugyanabba az osztlyba tartoznak, mint a kznsges azonostk, bels blokkokban jradeklarlhatk, de a bels deklarciban a tipust explicit mdon meg kell adni: typedef float distance; . . . { auto int distance; . . . Az int-nek szerepelnie kell a msodik deklarciban, klnben a fordt deklartor nlkli, distance tpus deklarcinak tekinten. 11.2. A kls azonostk rvnyessgi tartomnya Ha egy fggvny extern-knt deklarlt azonostra hivatkozik, akkor a teljes programot alkot llomnyok, ill. knyvtrak kzl valamelyikben szerepelnie kell az azonost kls defincijnak. Egy adott programban elfordul minden olyan fggvny, amely ugyanarra a kls azonostra hivatkozik, egyben ugyanarra az objektumra is hivatkozik, ezrt gyelnnk kell arra, hogy a definciban megadott tpus s mret kompatibilis legyen minden egyes, az adatokra hivatkoz fggvnyben megadott tpussal s mrettel. Az extern kulcssz a kls definciban azt jelzi, hogy a deklarlt azonostk szmra szksges trhelyet valamely msik llomnyban foglaljuk le. gy tbb llomnybl ll programban extern specifiktor nlkli kls adatdefinci egy s csakis egy llomnyban szerepelhet. Az sszes tbbi llomnyban, ahol kls defincival kvnjuk valamelyik vltozt megadni, a definciban szerepelnie kell az extern-nek. Az azonost csak abban a deklarciban inicializlhat, ahol a trhely lefoglalsa trtnt. A legfels szinten kls defincikban static-knt deklarlt azonostk ms llomnyokban nem lthatk. Fggvnyek is deklarlhatk static-knt.

31

12. A fordtnak szl vezrlsorok A C fordt rsze egy elfeldolgoz program, amely makrohelyettestsre, feltteles fordtsra s megadott nev llomnyok beiktatsra kpes. Az elfeldolgoz a # karakterrel kezdd sorokat rtelmezi. E sorok szintaxisa fggetlen a nyelv tbbi rsztl, brhol elfordulhatnak, s (rvnyessgi tartomnytl fggetlenl) hatsuk az adott forrsprogram-llomny vgig tart. 12.1. Szintaktikai egysgek helyettestse A #define azonost szint._egysgek_karakterlnca alak fordt vezrl sor (vigyzat: nincs zr pontosvessz) hatsra az elfeldolgoz az azonost minden tovbbi elfordulst a szintaktikai egysgek megadott karakterlncval helyettesti. A #define azonost(azonost, . . .,azonost) szint._egysgek_karakterlnca alak sor, ahol az els azonost s a ( kztt nincs szkz, argumentumokkal elltott makrodefinci. Az els azonostnak azon tovbbi elfordulsait, ahol az azonostt ( , szintaktikai egysgek vesszkkel elvlasztott sorozata s egy ) kveti, a definciban megadott szintaktikai egysg karakterlnccal helyettesti. A definci formlis paramterlistjban emltett azonost sszes elfordulsa helyre a hvs hatsra a megfelel szintaktikai egysg karakterlnc kerl. A hvs aktulis argumentumai vesszkkel elvlasztott szintaktikai egysg karakterlncok, azonban az idzjelek kztti vagy zrjelekkel vdett vesszk nem argumentumelvlasztk. A formlis s aktulis paramterek darabszma egyenl kell, hogy legyen. Karakterlncon vagy karakterllandn belli szvegre nem vonatkozhat a helyettests. A helyettest karakterlncot (mindkt vltozatban) jra tvizsglja az elfeldolgoz, hogy megtallja az esetleges tovbbi definilt azonostkat. A hossz defincik mindkt alakban j sorban folytathatk oly mdon, hogy a folytatand sor vgre \-t runk. A #define hasznlat_ leginkbb a hangslyozott funkcij llandk definilsra elnys, pl.: #define TABSIZE 100 int table[TABSIZE]; Az #undef azonost alak vezrlsor hatsra megsznik az azonost elfeldolgoz-defincija. 12.2. llomnyok beiktatsa Az #include llomnynv alak vezrlsort az elfeldolgoz program az llomnynv nev llomny teljes tartalmval helyettesti. A megnevezett llomny keresse az eredeti forrsllomny katalgusban kezddik, majd sorban, szabvnyos helyeken folytatdik. Megadhatjuk az

32

#include <llomnynv> alak vezrlsort is, amikor a keress csak a szabvnyos helyeken trtnik, s nem terjed ki a forrsllomny katalgusra. Az #include-ok egymsba skatulyzhatk. 12.3. Feltteles fordts Az #if lland_kifejezs alak fordtsvezrl sor ellenrzi, hogy az lland kifejezs (l. a 15. pontban) rtke nemnulla-e. Az #ifdef azonost alak vezrlsor megvizsglja, hogy az azonost pillanatnyilag definilva van-e az elfeldolgozban, azaz szerepelt-e mr valamelyik #define vezrlsorban. Az #ifndef azonost alak vezrlsor azt ellenrzi, hogy az azonost pillanatnyilag definilatlan-e az elfeldolgozban. Mindhrom alakot tetszleges szm, esetleg az #else vezrlsort is tartalmaz sor, majd az #endif vezrlsor kveti. Ha a vizsglt felttel igaz, akkor az #else s az #endif kztti sorok hatstalanok. Ha a vizsglt felttel hamis, akkor az ellenrzs s az #else vagy annak hinyban a #endif kztti sorok lesznek hatstalanok. E szerkezetek egymsba skatulyzhatk. 12.4. Sorvezrls Egyb, C programokat ltrehoz elfeldolgozk szempontjbl hasznos a #line lland_azonost alak sor. Hatsra - diagnosztikai clokbl a fordt azt hiszi, hogy a kvetkez forrssor sorszma az lland ltal megadott rtk, s a pillanatnyi bemeneti llomny az, amelyet az azonost megnevez. Azonost hinyban a megnevezett llomnynv nem vltozik. 13. Implicit deklarcik A deklarciban nem mindig kell a trolsi osztlyt s az azonostk tipust is megadnunk. A trolsi osztlyt kls defincikban s formlis paramterek ill. a struktratagok deklarciiban a szvegkrnyezet hatrozza meg. Fggvnyen belli deklarciban, ha a trolsi osztlyt megadtuk, de a tpust nem, az azonost felttelezs szerint int; ha tpus szerepel, de trolsi osztly nem, akkor az azonostt auto-nak tekinti a fordt. Az utbbi szably all kivtelek a fggvnyek, mivel az auto fggvnyeknek nincs rtelmk (a C nem kpes kdot generlni a verembe); ha valamely azonost tpusa fggvny, amely ...-t ad vissza, akkor az implicite extern-nek deklarldik. Kifejezsekben az olyan, mg nem deklarlt azonostt, amelyet ( kvet, a szvegkrnyezet alapjn a fordt int-et visszaad fggvnynek tekinti.

33

14. Mg egyszer a tpusokrl Ez a szakasz azokat a mveleteket foglalja ssze, amelyeket csak bizonyos tpus objektumokon lehet elvgezni. 14.1. Struktrk s unionok Struktrkkal s unionokkal kt dolgot tehetnk: megnevezhetjk valamelyik tagjukat (a . opertorral), vagy elllthatjuk a cmket (az egyoperandus &-tel). Az egyb mveletek, mint a struktrk vagy unionok valamihez trtn hozzrendelse, paramterknt val tadsa, vagy nekik val rtkads hibazenetet von maga utn. Remljk, hogy a jvben a C, ha egyebekkel nem is, de ezekkel a mveletekkel kiegszl. A 7.1. pontban mondottak szerint a ( . vagy -> segtsgvel trtn) direkt vagy indirekt struktrahivatkozsban a jobb oldalon ll nvnek a bal oldali kifejezs ltal megnevezett vagy megcmzett struktra tagjnak kell lennie. A rugalmas tpuskezels rdekben ezt a megktst a fordt kveteli meg szigoran. Valjban a . eltt brmilyen balrtk megengedett, s a fordt felttelezi, hogy ez a balrtk olyan alak struktra, mint amilyen a jobb oldali nv tagja. A -> eltti kifejezsnek ugyancsak mutatnak vagy egsznek kell lennie. Ha a kife_ezs mutat, akkor felttelezs szerint arra a struktrra mutat, amelyiknek a jobb oldalon ll nv tagja. Ha a kifejezs egsz tpus, akkor a fordt a megfelel struktra (gpi trolsi egysgekben kifejezett) abszolt cmnek tekinti. Az ilyen konstrukcik nem gpfggek. 14.2. Fggvnyek Fggvnnyel csupn kt mveletet vgezhetnk: meghvhatjuk vagy elllthatjuk a cmt. Ha a fggvny neve kifejezsen bell nem valamely hvs fggvnynv-pozcijn jelenik meg, akkor a fggvnyt megcmz mutat jn ltre. Ha teht egy fggvnyt egy msiknak akarunk tadni, azt mondhatjuk, hogy: int f (); . . . g (f); Ekkor a g defincija g (funcp) int (*funcp) (); { . . . (*funcp) (); . . . } lehet. Jegyezzk meg, hogy f-et a hv rutinban explicit deklarlni kell, mivel g (f)-beli elfordulst nem kvette (.

mdon

14.3. Tmbk, mutatk s indexels Minden alkalommal, amikor tmb tpus azonost jelenik meg egy kifejezsben, az azonost a tmb els elemt megcmz mutatv alakul t. E konverzi miatt a tmbk nem balrtkek. Definci szerint a [ ] indexopertor rtelmezse olyan, hogy E1 [E2] azonos *((E1)+(E2))

34

vel. A +-ra vonatkoz konverzis szablyok kvetkeztben, ha E1 tmb s E2 egsz, akkor E1 [E2] az E1 tmb E2-dik elemre hivatkozik. Emiatt - _aszimmetrikus megjelense ellenre - az indexels kommutatv mvelet. A tbbdimenzis tmbkre kvetkezetes szably vonatkozik. Ha E n-dimenzis, i * j * . . . * k-rang tmb , akkor kifejezsekben (n-1 )-dimenzis, j*...*k-rang tmbt megcmz mutatv alakul t. Ha a * opertor akr explicit, akr indexels kvetkeztben implicit mdon erre a mutatra alkalmazzuk, az eredmny a megcmzett (n-1 )-dimenzis tmb, amely maga is azonnal mutatv alakul t. Tekintsk pl. az int x [3][5]; deklarcit. Itt x 3*5-s egsz tmb. Ha x kifejezsben jelenik meg, akkor x a hrom darab 5-tag egsz tmb kzl az elst megcmz mutatv alakul t. Az x[i) kifejezsben, amely *(x+i)-vel egyenrtk, x elszr az ismertetett mdon mutatv, majd i az x tpusval azonos tpusv alakul, ami magban foglalja azt, hogy i megszorzdik annak az objektumnak a hosszval, amelyre a mutat mutat: ez jelen esetben 5 egsz objektum. Az eredmnyek sszeaddnak, s indirekci alkalmazsval (5 egszbl ll) tmb keletkezik, amely viszont ezen egszek kzl az elst megcmz mutatv alakul t. Ha mg tovbbi index is van, ismt ugyanezt a megfontolst kell alkalmazni; esetnkben az eredmny egsz. A fentiekbl kvetkezik, hogy a C-ben a tmbk sorfolytonosan troldnak (az utols index vltozik a leggyorsabban), tovbb, hogy a deklarciban elfordul els index segtsgvel hatrozhat meg a tmb ltal elfoglalt trterlet nagysga, egyb szerepe azonban az indexszmtsokban nincs. 14.4. Explicit mutatkonverzik A mutatkra bizonyos konverzik megengedettek ugyan, de gpfgg vonatkozsaik vannak. Valamennyi ilyen konverzit explicit tpuskonverzis opertorral rhatjuk el (l. a 7.2. s 8.7. pontot). Mutatk brmely olyan integrlis tpuss talakthatk, amelyben elfrnek. Az, hogy ez a tpus int vagy long-e, gpfgg. A lekpzs maga is gpfgg, de azok szmra, akik ismerik a gp cmzsi struktrjt, nem okozhat meglepetst. A ksbbiekben nhny gpre vonatkozan a rszleteket is ismertetjk. Az integrlis tpus objektumok explicit mdon mutatkk alakthatk t. A lekpzs hatsra a mutatkbl ltrejtt egszek ugyanazokk a mutatkk alakulnak vissza, egybknt a folyamat gpfgg. Adott tpust megcmz mutat ms tpust megcmz mutatv alakthat. Az eredmnyl kapott mutat cmzsi zavarokat okozhat, ha a szban forg mutat ltal megcmzett objektum illeszkedse a trban nem megfelel. Bizonyos azonban, hogy adott mret objektumot megcmz mutat vltozatlan marad, ha elszr kisebb mret objektumot, majd ismt az eredeti mret objektumot megcmz mutatv alaktjuk. A trterlet-foglal rutin pl. elfogadhatja valamely kiutaland objektum (byte-okban megadott) mrett s char mutatt adhat vissza:

35

extern char *alloc (); double *dp; dp = (double *) alloc (sizeof (double)); *dp = 22.0 / 7.0; Az alloc-nak (gpfgg mdon) biztostani kell, hogy a visszaadott rtket t lehessen alaktani double mutatv; ebben az esetben a fggvny hasznlata gpfggetlen. A PDP- 11 mutatbrzolsa 16 bites egsznek felel meg, egysge a byte. A char-okkal szemben nincsenek illeszkedsi kvetelmnyek; minden msnak pros cmnek kell lennie. A Honeywell 6000 gpen a mutat 36 bites egsznek felel meg: a szrsz a bal oldali 18 biten van, s az a kt bit, amely a szn bell a karaktert vlasztja ki, ettl kzvetlenl jobbra tallhat. Igy a karaktermutatkat a 216 byte-os egysgekben mrjk, minden ms 218 gpi sz egysgekben mrhet. A double mennyisgeknek s az azokat tartalmaz aggregtumoknak pros szcmen kell elhelyezkednik (0 mod 219). Az IBM 370 s az Interdata 8/32-es gpek hasonlak. A cmeket mindkettn byte-okban mrjk; az elemi objektumoknak a hosszuknak megfelel hatrra kell illeszkednik, gy a short-ot megcmz mutatknak (0 mod 2)-nek, az int-re s float-ra mutatknak (0 mod 4)nek s a double-ra mutatknak (0 mod 8)-nak kell lennik. Aggregtum illesztse az alkotelemeire vonatkoz illeszkedsi felttelek kzl a legszigorbb szerint trtnik. 15. lland kifejezsek A C nyelvben tbb helyen kell alkalmaznunk olyan kifejezseket, amelyeket kirtkelve lland eredmnyt kapunk: case utn, tmbhatrknt, kezdeti rtkekknt. Az els kt esetben a kifejezsben csupn egsz llandk, karakterllandk s sizeof kifejezsek szerepelhetnek, amelyeket a + - * / % & | ^ << >> == != < > <= >= kt-, ill a - ~ egyoperandus opertorok valamelyike vagy a hromoperandus ? : opertor kthet ssze egymssal. A zrjelek hasznlhatk, fggvnyhvsra azonban nem. csoportostsra

Kevesebb megkts vonatkozik a kezdeti rtkekre; az elbb trgyalt lland kifejezseken kvl kls s statikus objektumokra, valamint lland kifejezssel indexelt kls s statikus tmbkre is alkalmazhat az egyoperandus & opertor. Implicit mdon az egyoperandus &-et indexeletlen tmbk s fggvnyek megjelensekor ugyancsak alkalmazhatjuk. Az alapszably az, hogy a kezdeti rtkek kirtkelsvel vagy llandt, vagy pedig valamely mr korbban deklarlt kls vagy statikus objektum (esetleg llandval nvelt vagy cskkentett) cmt kell megkapnunk. 16. Gpfggetlensg A C nyelv bizonyos rszei lnyegknl fogva gpfggk. Az albbiakban nem trhettnk ki minden problmra, csak a legfontosabbakat akartuk kiemelni. Az olyan tisztn hardverkrdsek, mint a lebegpontos aritmetika tulajdonsgai s az szavak egszek mrete, osztsa a a

36

gyakorlatban nem okoztak klnsebb gondot. A hardver egyb jellegzetessgei az eltr megvalstsokban mutatkoznak meg. Ezek nmelyike, klnsen az eljel-kiterjeszts (negatv karakter negatv egssz trtn talaktsa), valamint a byte-ok szavakon belli elhelyezkedsi sorrendje olyan kellemetlen tnyezk, amelyekre klns figyelmet kell fordtanunk. Az egyb gpfgg tulajdonsgok mr nem jelentenek nagyobb problmt. A regiszterekben tnylegesen elhelyezked register tpus vltozk szma - a megengedett tpuskszlethez hasonlan - gprl gpre vltozik. Minden fordt helyesen vgzi azonban a dolgt a sajt gpe szempontjbl: a fls szm vagy rvnytelen register deklarcikat nem veszi figyelembe. Nehzsgek csak akkor tmadnak, amikor valaki rossz programozsi mdszereket alkalmaz. Ne rjunk olyan programokat, amelyek az adott architektra brmilyen specifikus tulajdonsgtl fggetlenek! A fggvnyargumentumok kirtkelsi sorrendjt a nyelv nem hatrozza meg. PDP- 11-en jobbrl balra, a tbbi gpen balrl jobbra trtnik. A mellkhatsok rvnyeslsnek sorrendje ugyancsak nem meghatrozott. Mivel a karakterllandk valjban int tpus objektumok, tbb karakterbl ll karakterllandk hasznlata is megengedett. Ennek megvalstsa azonban rendkvl gpfgg, mivel a karakterek szhoz trtn hozzrendelsnek sorrendje gprl gpre vltozik. Mezk hozzrendelse szavakhoz, karakterek egszekhez a PDP 11-en jobbrl balra, a tbbi gpen balrl jobbra trtnik. Elszigetelt programok szmra e klnbsgek lthatatlanok maradnak, hacsak nem viszik tlzsba a tpusokkal folytatott jtkot (pl. azltal, hogy valamely int mutatt char mutatv alaktanak t, majd megvizsgljk a megcmzett trterletet). Szmolnunk kell azonban e klnbsgekkel akkor, ha a programunkat kvlrl megszabott trterletelrendezsekkel akarjuk sszhangba hozni. A klnfle fordtk ltal elfogadott nyelvek csupn egszen kis rszletekben trnek el egymstl. A leglnyegesebb, hogy a pillanatnyilag hasznlatos PDP-11-es fordt nem inicializlja a bitmezket tartalmaz struktrkat, s egyes rtkad opertorokat nem fogad el olyan krnyezetben, ahol ki akarjuk hasznlni a hozzrendels rtkt. 17. Anakronizmusok Mivel a C fejldsben lev nyelv, egyes rgebbi programokban bizonyos elavult szerkezetek tallhatk. Br a fordt legtbb vltozata az ilyen anakronizmusokat is tmogatja, elbb-utbb ezek el fognak tnni, csupn gpfggsgi problmt hagyva maguk utn. A C nyelv korbbi vltozatai rtkad opertorknt az =op alakot hasznltk az op= alak helyett. Ez ktrtelmsgekhez vezet, amelynek tipikus esete x = -1 amely a valsgban x-et dekrementlja, mivel az = s a szomszdosak, de amivel knnyen az lehetett a szndkunk, hogy 1-et rendeljnk x-hez. A kezdeti rtkek szintaxisa megvltozott: korbban a kezdeti rtket bevezet egyenlsgjel nem szerepelt, gy az

37

int x = 1; alak helyett az int x 1; alak volt hasznlatban. A vltoztats azrt trtnt, mert az int f (1+2) alak inicializls ppen elgg hasonlt fggvnydeklarcira ahhoz, hogy megtvessze a fordtkat. 18. A szintaxis sszefoglalsa A C nyelv szintaxisnak sszefoglalsa sokkal inkbb segdletl, mintsem a nyelv rvid sszefoglalsul szolgl. 18.1. Kifejezsek Az alapvet kifejezsek a kvetkezk: kifejezs: elsdleges_kifejezs *kifejezs &kifejezs kifejezs !kifejezs ~kifejezs ++balrtk balrtk balrtk ++ balrtk sizeof kifejezs (tpus_nv) kifejezs kifejezs ktop kifejezs kifejezs ? kifejezs : kifejezs balrtk rtkad_op kifejezs kifejezs , kifejezs elsdleges_kifejezs: azonost lland karakterlnc ( kifejezs ) elsdleges_kifejezs ( kifejezs_listaopc) elsdleges_kifejezs [ kifejezs ] balrtk . azonost elsdleges_kifejezs -> azonost balrtk: azonost elsdleges_kifejezs [ kifejezs ] balrtk . azonost elsdleges_kifejezs -> azonost *kifejezs ( balrtk ) A () [] . -> elsdleges kifejezs opertorok prioritsa a legmagasabb, s az ilyen opertorok balrl jobbra ktnek. Az egyoperandus & - ! ~ ++ -- sizeof (tpusnv) opertorok prioritsa az elsdleges opertoroknl alacsonyabb, de magasabb az sszes ktoperandus opertornl: ezek az opertorokjobbrl balra ktnek. Az sszes ktoperandus opertor s a a

tmr

38

feltteles opertor balrljobbra kt, ezeket az albbiakban cskken prioritsi sorrendben soroljuk fel: ktop: * / % + >> << < > <= >= == != & ^ | && || ? : Az rtkad opertorok mindegyike azonos priorits, s mindegyik jobbrl balra kt. rtkad_op: = += -= *= /= %= >>= <<= &= ^= |= A vessz opertor (,) prioritsa a legalacsonyabb, s balrl jobbra csoportost. 18.2. Deklarcik deklarci: dekl._specifiktorok deklartorlistaopc; dekl._specifiktorok: tpus_specifiktor dekl._specifiktorokopc t.o._specifiktor dekl._specifiktorokopc t.o._specifiktor: auto static extern register typedef tpus_specifiktor: char short int long unsigned float double strukt._vagy_union_specifiktor typedef_nv k.._deklartorlista: k.._deklartor k.._deklartor , k.._deklartorlista k.._deklartor: deklartor inicializlopc deklartor: azonost ( deklartor ) *deklartor deklartor () deklartor [ strukt._vagy_union_specifiktor: struct { strukt._dekl._lista }

lland_kifejezsopc]

39

struct azonost { strukt._dekl._lista } struct azonost union { strukt._dekl._lista } union azonost {strukt._dekl._lista } union azonost strukt._dekl._lista: strukt._deklarci strukt._deklarci strukt._dekl._lista strukt._deklarci: tpus_specifiktor strukt._deklartor_lista: strukt._deklartor strukt._deklartor , strukt._deklartor_lista strukt._deklartor: deklartor deklartor : lland_kifejezs : lland_kifejezs inicializl: = kifejezs = { inicializl_lista } = { inicializl_lista, } inicializl_lista: kifejezs inicializl_lista , inicializl_lista { inicializl_lista } tpus_nv: tpus_specifiktor absztrakt_deklartor absztrakt_deklartor: res ( absztrakt_deklartor ) *absztrakt_deklartor absztrakt_deklartor () absztrakt_deklartor [ lland_kifejezsopc] typedef_nv: azonost 18.3. Utastsok sszetett_utasts: { deklarcilistaopc utastslistaopc } deklarcilista: deklarci deklarci deklarcilista utastslista: utasts utasts utastslista utasts: sszetett_utasts kifejezs; if ( kifejezs ) utasts if ( kifejezs ) utasts else utasts strukt._deklartor_lista;

40

while ( kifejezs ) utasts do utasts while ( kifejezs ) ; for (kifejezs_1opc; kifejezs_2opc; kifejezs_3opc) utasts switch ( kifejezs ) utasts case lland_kifejezs: utasts default: utasts break; continue; return; return kifejezs; goto azonost; azonost: utasts ; 18.4. Kls defincik program: kls_definci kls_definci program kls_definci: fggvnydefinci adatdefinci fggvnydefinci: dekl._specifiktoropc fggvnydeklartor: azonost azonost , paramterlista fggvnytrzs: deklarcilista fggvny_utasts fggvny_utasts: deklarcilistaopc utastslista adatdefinci: externopc tpus_specifiktoropc k.._deklartorlista; tpus_specifiktoropc k.._deklartorlista; 18.5. Elfeldolgoz #define azonost szint._egysgek_karakterlnca azonost(azonost, ..., azonost) szint._egysgek_karakterlnca #undef azonost #include llomnynv #include <llomnynv> #if lland_kifejezs #ifdef azonost #ifndef azonost #else #endif staticopc fggvnydeklartor fggvnytrzs

deklartor ( paramterlistaopc ) paramterlista:

#define

41

#line lland azonost _ 1. fejezet: Alapismeretek

A C nyelv tanulst kezdjk az alapismeretek gyors elsajttsval. Clunk az, hogy mkdkpes programokon, de a rszletekbe, formlis szablyokba s kivtelekbe val belebonyolds nlkl mutassuk be a nyelv legfontosabb elemeit. Nem treksznk teht teljessgre, st pontossgra sem (eltekintve attl, hogy a pldknak helyeseknek kell lennik). Az olvasnak a lehet leggyorsabban el kell jutnia addig a pontig, ahol mr hasznlhat programokat tud rni. ppen ezrt bevezetnk az alapvet tudnivalkra koncentrl: a vltozkra, az llandkra, az aritmetikra, a vezrlstadsra, a fggvnyekre, a be- s kivitellel kapcsolatos elemi ismeretekre. Tudatosan kihagytuk ebbl a fejezetbl a C nyelv olyan sszetevit, amelyek nagyobb programok rsnl ltfontossgak. Ilyenek a mutatk, a struktrk, a C nyelv gazdag opertorkszletnek legnagyobb rsze, nhny vezrlstad utasts s mg ezernyi rszlet. Ennek a megkzeltsnek megvannak termszetesen a maga htrnyai is. Ezek kzl a legfontosabb, hogy a nyelv valamely sszetevjt ler sszes informci nem egy helyen tallhat, s a bevezet fejezet rvidsgnl fogva flrevezet lehet. Tovbb, mivel nem hasznlhat a C nyelv egsz fegyvertra, a pldk nem olyan tmrek s elegnsak, mint lehetnnek. Ezeket a htrnyokat igyekeztnk a minimlisra cskkenteni, de minderrl azrt ne feledkezznk meg. Tovbbi htrny, hogy a bevezet egyes rszei a ksbbiekben szksgszeren megismtldnek. Remljk, hogy ez az ismtls inkbb segti, mintsem bosszantja az olvast. Tapasztalt programozk mindenesetre mr ennek a fejezetnek az anyagbl ki tudjk kvetkeztetni a szmukra szksges programozsi informcit. Kezdknek ajnljuk, hogy maguk is rjanak az itt bemutatottakhoz hasonl, kismret programokat. A bevezet mindkt csoportnak keretknt szolglhat a ksbbi fejezetek anyagnak befogadshoz. 1.1. Induls Egy j programnyelv elsajttsnak egyetlen mdja, ha programokat runk az adott nyelven. Az els megrand program minden nyelv tanulsakor hasonl szokott lenni : Nyomtassuk ki a Figyelem, emberek! szavakat. Ez az els akadly. Ahhoz, hogy tugorjuk, kpesnek kell lennnk arra, hogy (valahol) ltrehozzuk a programszveget, sikeresen lefordtsuk, betltsk, lefuttassuk, s ki kell tallnunk, hov kerl a kivitt szveg. Ha ezeken a_mechanikus rszleteken tljutottunk, minden ms viszonylag knnyen fog menni. C nyelven a Figyelem, emberek! szveget kinyomtat program a kvetkez: main () { printf (Figyelem, emberek! \ n); } A program futtatsnak mdja az ppen hasznlt rendszertl fgg.

42

Az UNIX opercis rendszerben pl. a forrsprogramot olyan llomny alakjban kell ltrehozni, amelynek a neve .c-re vgzdik, mint pldul figyel.c, majd ezt a cc figyel.c paranccsal le kell fordtani. Ha nem kvettnk el semmilyen hibt, pl. nem hagytunk ki egy karaktert, vagy nem rtunk valamit hibsan, a fordts rendben vgbemegy s egy vgrehajthat llomny keletkezik, amelynek neve a.out . Ha ezt az a.out paranccsal lefuttatjuk, akkor a Figyelem, emberek! szveg jelenik meg a kimeneten. Ms opercis rendszerekben a szablyok eltrek, ilyen esetben forduljunk megfelel szakemberhez. 1. 1. Gyakorlat. Futtassa le a fenti programot a sajt rendszern! Ksrletezzen a program egyes rszeinek elhagysval, hogy meglssa, milyen hibazenetek rkeznek! Most pedig nhny megjegyzs magrl a programrl. A C programok mretktl fggetlenl egy vagy tbb fggvnyt tartalmaznak, amelyek meghatrozzk az ppen elvgzend szmtsi mveleteket. A C-beli fggvnyek a FORTRAN fggvnyeihez vagy szubrutinjaihoz, ill. a PL/1 , a PASCAL stb. eljrsaihoz hasonltanak. Pldnkban a main ilyen fggvny. A fggvny neve ltalban tetszleges lehet, de a main specilis nv programunk vgrehajtsa mindig a main elejn kezddik. Ebbl az kvetkezik, hogy minden programban el kell hogy forduljon egy main valahol. A main a feladat vgrehajtsa rdekben ltalban ms fggvnyeket fog meghvni, amelyek kzl egyesek ugyanabban a programban szerepelnek, mg msok elzleg megrt fggvnyknyvtrakbl szrmaznak. A fggvnyek kztti adattads egyik mdja az argumentumok hasznlata. A fggvnynevet kvet zrjelek az argumentumlistt hatroljk: jelen esetben main argumentum nlkli fggvny, amit () jell. A { } kapcsos zrjelek a fggvnyt alkot utastsokat zrjk kzre; szerepk a PL/1beli do-end-del, az ALGOL s PASCAL begin-end-jvel azonos. A fggvny hvsa a fggvny megnevezsvel trtnik, amit az argumentumok zrjelezett listja kvet. A FORTRAN-tl vagy PL/1-tl eltren itt nincs call utasts. A zrjeleknek akkor is szerepelnik kell, ha egyetlen argumentum sincs. A printf (Figyelem, emberek!\ n); sor nem ms, mint fggvnyhvs, amely a printf nev fggvnyt hvja a Figyelem, emberek!\ n argumentummal. printf knyvtri fggvny, amely az eredmnyt esetnkben az argumentumt alkot karakterlncot - (egyb perifria megadsa hjn) a terminlra rja. Egy tetszleges szm karakterbl ll, idzjelek () kz zrt karaktersorozatot karakterlncnak, karakter-llandnak (stringnek, ill. stringkonstansnak) neveznk. Pillanatnyilag a karakterlncokat csak a printf s ms fggvnyek argumentumaiknt hasznljuk.

43

karakterlncban elfordul \ n karaktersorozat az jsor

karakter C-beli jellsmdja. Hatsra a kirs a kvetkez sor bal szln folytatdik. Ha a \ n-et elhagyjuk (rdemes megksrelni), azt tapasztaljuk, hogy a kirs vgn a kocsi vissza -soremels elmarad. Az jsor karaktert csakis egy \n segtsgvel iktathatjuk be a printf-be: ha valami olyasmivel prblkozunk, mint printf (Figyelem, emberek! ); akkor a C fordt bartsgtalan hinyz idzjelekrl. zeneteket fog kldeni bizonyos

A printf sohasem helyez el jsor karaktert automatikusan, gy tbbszri hvs segtsgvel egyetlen kimeneti sort fokozatosan rakhatunk ssze. Els programunkat gy is rhattuk volna: main () { printf (Figyelem, ); printf (emberek!); printf (\ n); } miltal a korbbival azonos kimenetet kaptunk volna. Megjegyezzk, hogy \ n egyetlen karaktert jelent. A \ n-hez hasonl, n. escape jelsorozatok ltalnosan hasznlhat s bvthet mechanizmust alkotnak nehezen elllthat vagy lthatatlan karakterek jellsre. A C nyelvben ilyenek mg a \t a tabultor, a \b a visszalptets (backspace), a \ az idzjel s a \\ magnak a fordtott trtvonalnak (backslash) a jellsre. 1.2. Gyakorlat. Prblja ki, mi trtnik, ha a printf argumentum-karakterlnca \ x-et tartalmaz, ahol x egy, a fenti listban nem szerepl karakter! 1.2. Vltozk s aritmetika Az albbi program a kvetkez Fahrenheit-hmrskleteket s a megfelel Celsius-rtkeket tartalmaz tblzatot nyomtatja ki a C = (5/9)(F-32) kplet alkalmazsval. 0 -17.8 20 -6.7 40 4.4 60 15.6 ... ... 260 126.7 280 137.8 300 148.9 me maga a program: /* Fahrenheit-Celsius tblzat kinyomtatsa f = 0, 20, . . ., 300 rtkekre */ main () { int lower, upper, step;

44

float fahr, celsius; lower = 0; /* A hmrsklet-tblzat als hatra */ upper = 300; /* fels hatr */ step = 20; /* lpskz */ fahr = lower; while (fahr <= upper) { celsius = (5.0 / 9.0) * (fahr - 32.0); printf (%4.0f %6.1f \n, fahr, celsius); fahr = fahr + step; } } Az els kt sor: /* Fahrenheit-Celsius tblzat kinyomtatsa f = 0, 20, . . ., 300 rtkekre */ egy megjegyzs (comment), amely esetnkben rviden elmondja, hogy mit csinl a program. A fordt minden, a /* s */ kztt elfordul karaktert figyelmen kvl hagy; gy ide tetszleges, a program megrtst segt szveget berhatunk. Megjegyzsek mindentt elfordulhatnak, ahol szkz vagy jsor elfordulhat. A C nyelvben hasznlat eltt minden vltozt deklarlni kell, ltalban a fggvny elejn, az els vgrehajthat utasts eltt. Ha errl megfeledkeznk, hibazenetet kapunk a fordttl. A deklarci egy tpus megadsbl s az illet tpus vltozk felsorolsbl ll. Plda erre az elbbi program kt sora: int lower, upper, step; float fahr, celsius; Az int tpus azt jelenti, hogy a felsorolt vltozk egsz (integer) tpusak. float jelli a lebegpontos (floating point) vltozkat, vagyis az olyan szmokat, amelyeknek trt rszk is van. Mind az int, mind a float szmok pontossga az adott szmtgptl fgg. A PDP-11 -en pldul az int 16 bit-es eljeles szm, vagyis olyan szm, amelynek rtke -32768 s +32767 kztt van. A float szm 32 bites mennyisg, ami krlbell 7 rtkes szmjegyet jelent, 10-38 s 1038 kztti nagysgrendben. A 2. fejezet ms gpekre is kzli a szmbrzolsi tartomnyokat. Az int s float mellett a C nyelvben ms alapvet adattpusok is vannak: char karakter egyetlen byte, short rvid egsz, long hossz egsz, double duplapontossg lebegpontos szm. Ezen objektumok mretei ugyancsak gpfggek, a rszleteket a 2. fejezet tartalmazza. Ezekbl az alapvet tpusokbl tmbk, struktrk s unionok kpezhetk, mutatk mutathatnak rjuk, fggvnyek trhetnek vissza a hvhoz ezekkel a tpusokkal: mindezekkel rvidesen tallkozunk. A hmrsklet-tszmt programban a tnyleges szmts a lower = 0; upper = 300; step = 20; fahr = lower; rtkad utastsokkal kezddik, amelyek a vltozk kezdeti rtkt lltjk be. Az egyes utastsokat pontosvessz zrja le. A tblzat minden sort azonos mdon kell kiszmtani, ezrt egy ciklust hasznlunk, amely tblzatsoronknt egyszer ismtldik; ez a clja a while utastsnak : while (fahr <= upper) {

45

... } Programfuts kzben a gp megvizsglja, teljesl-e a zrjelek kztti felttel. Ha az rtke igaz (fahr kisebb vagy egyenl, mint upper), akkor vgrehajtja a ciklustrzs (a { s } kapcsos zrjelek kz zrt) utastsait. Ezutn ismt megvizsglja a felttelt, s ha az rtke igaz, jra vgrehajtja a trzset. Ha a vizsglat a hamis logikai rtket szolgltatja (fahr meghaladja upper-t), akkor a ciklus lezrul s a vgrehajts a ciklust kvet els utastson folytatdik. Az adott program nem tartalmaz tbb utastst, teht a program vget r. A while trzse egy vagy tbb, kapcsos zrjelek kz zrt utasts lehet, mint a hmrsklet-tszmt programban, vagy egyetlen, kapcsos zrjel nlkli utasts, mint pl.: while (i < j) i = 2 * i; A while ltal vezrelt utastsokat mindkt esetben kt pozcival beljebb rtuk, hogy els pillantsra vilgos legyen, mely utastsok helyezkednek el a cikluson bell. A bekezds a program logikai szerkezett hangslyozza. Br a C nyelv meglehetsen ktetlen az utastsok pozcionlst illeten, ha azt akarjuk, hogy programunk knnyen olvashat legyen, nagyon fontos a megfelel bekezdsek s res helyek hasznlata. Clszer, ha egy sor egy utastst tartalmaz, s (ltalban) hagyjunk egy-egy szkzt az opertorok eltt s utn. A zrjelek pozcija kevsb lnyeges: e tekintetben a tbbfle divatos stlus egyikt vlasztottuk. Az olvas brmilyen neki megfelel stlus mellett dnthet, clszer azonban, ha ezt azutn kvetkezetesen hasznlja. A munka nagyja a ciklus trzsben kszl el. A celsius = (5.0 / 9.0) * (fahr - 32.0); utastssal kiszmtjuk a Celsius-fokokban kifejezett hmrskletet, s rtkt hozzrendeljk a celsius vltozhoz. Az ok, ami miatt 5.0 / 9.0-t hasznltunk, 5 / 9 helyett az, hogy a C nyelv csakgy, mint sok ms nyelv, az egsz szmokkal vgzett osztsnl az eredmny trt rszt elhagyja. Teht 5 / 9 rtke 0, s 0 lenne az sszes hmrsklet is. Az llandn belli tizedespont jelzi, hogy az illet lland lebegpontos, gy 5.0 / 9.0 rtke 0.555..., amire szksgnk van. Ugyancsak 32.0-t rtunk 32 helyett, noha mivel a fahr vltoz float 32 automatikusan float-t (32.0-v) alakulnak t a kivons eltt. Blcsebb azonban azt a stlust kvetni, hogy a lebegpontos llandkat tizedesponttal rjuk akkor is, ha az rtkk egsz : ezzel az olvas szmra hangslyozzuk ezek lebegpontos termszett, s biztostjuk, hogy a fordt is eszerintkezelje ket. A 2. fejezet rszletesen tartalmazza annak szablyait, hogy az egsz szmok mikor alakulnak t lebegpontoss. Egyelre csak annyit jegyznk meg, hogy a fahr = lower; rtkad utasts s a while (fahr <= upper) vizsglat egyarnt a vrt mdon mkdik (az int a mvelet elvgzse eltt float-t alakul t). Ez a plda a printf mkdsbl is valamivel tbbet mutat meg. A printf ltalnos cl formtumkonvertl fggvny, amelyet teljes egszben majd a 7. fejezetben ismertetnk. Els

46

argumentuma a kinyomtatand karakterlnc, ahol az egyes %-jelek mutatjk, hogy hov kell a tovbbi (msodik, harmadik, . . .) argumentumokat behelyettesteni s milyen formtumban kell azokat kinyomtatni. Pldul a printf (%4.0f %6.1f \n, fahr, celsius); utastsban a %4.0f konverzi-elrs szerint a lebegpontos szmot egy legalbb ngy karakter szles helyre kell berni gy, hogy a tizedespontot nem kvetik szmjegyek. %6.1f egy msik szmot r le, amely legalbb 6 szkzt foglal el s a tizedespont utn 1 szmjegyet tartalmaz hasonlan a FORTRAN-beli F6.1 vagy a PL/1-beli F(6,1) alakhoz. A specifikci egyes rszei elhagyhatk:%6f azt rja el, hogy a szm legalbb 6 karakter szles; %.2f legalbb kt helyet ignyel a tizedespont utn, de a szlessget nem korltozza, s %f egyszeren azt mondja, hogy a szmot lebegpontosknt kell kinyomtatni. Hozztehetjk, hogy a printf a %d-t decimlisknt, %o-t oktlisknt, %x-et hexadecimlisknt, %c-t karakterknt, %s-et karakterlncknt s %%-ot %-knt rtelmezi. A printf els argumentumban tallhat minden egyes % konstrukcihoz hozzrendeldik a neki megfelel msodik, harmadik stb. argumentum; ezeknek szm szerint s tpus szerint is meg kell egyeznik, ellenkez esetben rtelmetlen vlaszokat kapunk. Egybknt a printf nem rsze a C nyelvnek: a C nyelven bell a be- s kivitel nincs definilva. A printf-ben nincs semmi rendkvli: csupn egy hasznos fggvny, amely rsze a C programok ltal kznsgesen elrhet szabvnyos rutin-knyvtrnak. Azrt, hogy magra a C nyelvre koncentrlhassunk, nem foglalkozunk tl sokat a be- s kivitellel, egszen a 7. fejezetig. Elssorban a formtumozott adatbevitel krdst halasztjuk el addig. Ha szmokat kell beolvasnunk, olvassuk el a 7. fejezet 7.4. szakasznak a scanf fggvnyrl szl rszt. A scanf - az adatmozgs irnytl eltekintve - nagyon hasonl a printf fggvnyhez. 1.3. Gyakorlat. Mdostsuk gy a hmrsklet-tszmt programot, hogy az a tblzat fl fejlcet is nyomtasson! 1.4. Gyakorlat. Irjunk programot a korbbi pldnak megfelel Celsius-Fahrenheit tblzat kinyomtatsra! 1.3. A for utasts Az olvas is nyilvn tudja, hogy egy programot sokflekppen meg lehet rni: prblkozzunk meg a hmrsklet-tszmt program egy msik vltozatval : main () / * Fahrenheit-Celsius tblzat*/ { int fahr; for (fahr = 0; fahr <= 300; fahr = fahr + 20) printf (%4d %6.1f \n, fahr, (5.0 / 9.0) * (fahr - 32)); } Ez ugyanazokat az eredmnyeket adja, de lthatan mskpp nz ki. Az egyik f eltrs, hogy a legtbb vltoz szksgtelenn vlt: csak a fahr maradt meg int vltozknt (azrt, hogy mutassa a printf-ben a %d konverzit). Az als s a fels hatr, valamint a lpskz csak llandknt jelenik meg a for utastsban, amely maga is j szmunkra. A Celsius-hmrskletet szmt kifejezs most nem kln rtkad utastsknt, hanem mint a printf harmadik argumentuma szerepel.

47

Ez az utbbi mdosts egy egszen ltalnos C-beli szablyon alapul, amely kimondja, hogy minden olyan sszefggsben, ahol valamely adott tpus vltoz rtkt hasznlhatjuk, ugyanolyan tpus kifejezst is hasznlhatunk. Minthogy a printf harmadik argumentumnak a %6.1f-re val illeszkeds rdekben lebegpontosnak kell lennie, tetszleges lebegpontos kifejezs is elfordulhat ezen a helyen. Maga a for egy ciklusutasts, a while ltalnostsa. Ha az elbbi while-lal sszehasonltjuk, mkdse rgtn vilgoss vlik. Hrom rszt tartalmaz, amelyeket pontosvesszk vlasztanak el. Az els rsz, vagyis fahr = 0 egyszer hajtdik vgre a ciklusba val belps eltt. A msodik rsz a ciklust vezrl ellenrzs vagy felttel: fahr <= 300 A gp megvizsglja a felttelt; ha igaz, akkor vgrehajtja a ciklus trzst (itt egyetlen printf), amit az jrainicializl lps, azaz fahr = fahr + 20 s jabb felttelvizsglat kvet. A ciklus akkor r vget, amikor a felttel hamiss vlik. Csakgy, mint a while esetben, a trzs vagy egyetlen utasts, vagy pedig kapcsos zrjelek kz zrt utastsok csoportja. Az inicializl s jrainicializl rsz egy-egy tetszleges kifejezs lehet. A while s a for kztt szabadon vlaszthatunk aszerint, hogy mi tnik vilgosabbnak. A for alkalmazsa ltalban olyan ciklusok esetben clszer, amelyekben az inicializls s jrainicializls egy-egy logikailag sszefgg utasts, mivel a for sokkal tmrebb, mint a while s egy helyen tartja a ciklusvezrl utastsokat. 1.5. Gyakorlat. Mdostsuk gy a hmrsklet-tszmt programot, hogy az a tblzatot fordtott sorrendben, teht 300 foktl 0 fokig nyomtassa ki! 1.4. Szimbolikus llandk Mg egy megjegyzs, mieltt elbcsznnk a hmrsklettszmtstl. Nem j gyakorlat, ha bvs szmokat, pldul 300-at vagy 20-at ptnk be a programba: ezek nem sokat mondanak annak, aki ksbb olvassa majd a programot, s megvltoztatsuk is nagyon nehz. Szerencsre a C nyelv lehetsget ad az ilyen bvs szmok elhagysra. A #define szerkezet segtsgvel a program elejn szimbolikus nevet vagy szimbolikus llandt rendelhetnk egy-egy megadott karakterlnchoz. Ezekutn a fordt a nv mindennem idzjelezett elfordulst a megfelel karakterlnccal helyettesti. A nv nemcsak szmot, hanem tetszleges szveget is helyettesthet, pl. : #define LOWER 0 /* A tblzat als hatra*/ #define UPPER 300 / * A tblzat fels hatra*/ #define STEP 20 /* Lpsnagysg*/ main () /*Fahrenheit-Celsius tblzat*/ { int fahr; for (fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP) printf(%4d %6.1f\n, fahr, (5.0/9.0) * (fahr-32)); } A LOWER, UPPER s STEP mennyisgek llandk, gy deklarciban nem jelennek meg. A szimbolikus neveket nagybetkkel szoks rni, gy azonnal megklnbztethetk a kisbets vltoznevektl. gyeljnk

48

arra, hogy a defincik utn nincs pontosvessz! Mivel a nevet kvet teljes sor behelyettestdik, a for-ban tl sok pontosvessz lenne. 1.5. Nhny hasznos program A kvetkezkben ttekintnk nhny egymssal sszefgg programot, amelyek karakteradatokon vgeznek egyszer mveleteket. Ki fog derlni, hogy sok program csupn az itt kzlt prototpusok bvtett vltozata. Karakterek be- s kivitele A szabvnyos knyvtrban rendelkezsre llnak olyan fggvnyek, amelyekkel egyszerre egy karaktert lehet rni vagy olvasni. A getchar() minden egyes hvsakor beolvassa a kvetkez bemeneti karaktert s a visszatrsi rtke ez a karakter lesz. Teht c = getchar() utn a c vltoz a kvetkez bemeneti karaktert tartalmazza. A karakterek kznsges esetben a terminlrl rkeznek, de ezzel a 7. fejezetig nem kell trdnnk. A putchar fggvny a getchar ellentte: putchar a c vltoz tartalmt valamilyen kimeneti perifrira rja ki, ami ltalban ismt a terminl. A putchar s a printf hvsai keverhetk: a kivitt karakterek a hvs sorrendjben fognak megjelenni. Hasonlan a printf-hez, a getchar s putchar fggvnyekben sincs semmi rendkvli. Ezek nem rszei a C nyelvnek, de mindentt rendelkezsre llnak. llomnymsols getchar s putchar birtokban meglepen sok hasznos programot rhatunk anlkl, hogy ezen kvl brmi egyebet tudnnk a be- s kivitelrl. A legegyszerbb plda az a program, amely a bemenetet karakterenknt a kimenetre msolja. A program vza: egy karakter beolvassa llomny vge jel) egy j karakter beolvassa Mindezt C nyelven kifejezve: main() /*A bemenet tmsolsa a kimenetre. 1. vltozat*/ { int c ; c = getchar(); while (c != EOF) { putchar; c = getchar(); } } A != relcis opertor jelentse : nem egyenl . A f problma a bemenet vgnek az rzkelse. Megllapods szerint a getchar az llomny vgnek megtallsakor olyan rtkkel tr vissza, amely nem valamely rvnyes karakter kdja: ily mdon a program szlelni tudja, hogy mikor fogytak el a _bemeneten a karakterek. Az egyetlen problma - ami azonban igen bosszant -, hogy ktfle megllapods is kzforgalomban van while (a beolvasott karakter nem az

az ppen beolvasott karakter kimenetre rsa

49

arra nzve, hogy valjban mi az llomny vge rtk. Ezt a problmt egyelre azzal kerltk ki, hogy a szmszer rtk helyett az EOF szimbolikus nevet hasznltuk, fggetlenl a tnyleges rtktl. A gyakorlatban EOF vagy -1 , vagy 0, a programot ennek megfelelen vagy #define EOF -1 vagy #define EOF 0 kell, hogy megelzze ahhoz, hogy helyes mkdst kapjunk. Azltal, hogy az EOF szimbolikus llandt hasznltuk annak az rtknek a jellsre, amit llomny vge esetn a getchar visszaad, elrtk, hogy az egsz programban csupn egyetlen dolog fgg az llomny vge tnyleges rtktl. A c vltozt int-nek s nem char-nak deklarltuk, gy abban trolhat a getchar ltal visszaadott rtk. Mint azt a 2. fejezetben ltni fogjuk, ez a vltoz azrt int tpus, mert alkalmasnak kell lennie arra, hogy az sszes lehetsges karakteren kvl az EOF rtket is felvegye. Gyakorlott C programozk a msolprogramot tmrebben rnk le. A C nyelvben az olyan rtkadsok, mint c = getchar() kifejezsekben is hasznlhatk; a kifejezs rtke egyszeren a bal oldalhoz hozzrendelt rtk. Ha a c-re vonatkoz rtkads egy while felttelvizsgl rsznek belsejbe kerl, akkor az llomnymsol program a kvetkezkppen rhat : main() /*A bemenet tmsolsa a kimenetre - 2. vltozat*/ { int c; while ((c = getchar()) != EOF) putchar ; } A program beolvas egy karaktert, hozzrendeli c-hez, majd ellenrzi, hogy a karakter azonos-e az llomny vge jellel. Ha nem, akkor a programfuts a while trzsnek vgrehajtsval, azaz a karakter kinyomtatsval folytatdik. Ezutn a while ciklus ismtldik. Ha a program vgl elri a bemeneti karaktersorozat vgt, akkor a while s vele egytt a main is befejezdik. Ez a vltozat egy helyre vonja ssze a beolvasst - most csak egy getchar hvs van -, s egyben le is rvidti a programot. Az rtkads behelyezse a felttelvizsglatba az egyik olyan eset, amikor a C nyelv hasznos tmrtst tesz lehetv. (Megvan persze a lehetsge annak, hogy ezt tlzsba vigyk s ttekinthetetlen programkdot hozzunk ltre, de ezt igyeksznk elkerlni.) Lnyeges ltnunk, hogy az rtkads krli zrjelek a felttelen bell tnyleg szksgesek. A != precedencija magasabb, mint az = szimblum ami azt jelenti, hogy zrjelek hinyban a != relcivizsglat megelzn az = rtkadsi mvelet vgrehajtst. Igy a c = getchar() != EOF utasts egyenrtk a

50

c = (getchar() != EOF) utastssal. Ez azzal a nemkvnatos eredmnnyel jr, hogy c 0 vagy 1 lesz, attl fggen, hogy a getchar hvsakor llomny vge jel rkezett-e vagy sem. (Errl rszletesebben a 2. fejezetben szlunk.) Karakterszmlls A kvetkez program, amelyet a msolprogram kaptunk, megszmllja a beolvasott karaktereket: main() { long nc; nc = 0; while (getchar () != EOF) ++nc; printf(%ld\n, nc); } A ++nc; utasts egy j opertort mutat be, amelynek jele ++, s a jelentse: inkrementlj eggyel. Irhatnnk azt is, hogy nc = nc + 1, de ++nc tmrebb s gyakran hatkonyabb is. Ltezik egy ennek megfelelopertor, amely 1-gyel dekrementl. A ++ s a egyarnt lehet prefix (eltag) opertor (++nc) vagy postfix (uttag) opertor (nc++)- e kt alakhoz kifejezsekben klnbz rtkek tartoznak, amint azt a 2. fejezetben ltni fogjuk, de ++nc s nc++ egyarnt inkrementlja nc-t. Egyelre megmaradunk a prefix opertornl. A karakterszmll program a karakterek szmt int helyett egy long tpus vltozban trolja. A PDP-11-en egy int mennyisg maximlis rtke 32767, gy a szmll viszonylag kevs bemen rtk esetn is tlcsordulna, ha int-nek deklarlnnk. A Honeywell s IBM C-ben a long s az int ugyanaz, de a maximlis rtk sokkal nagyobb. A %ld konverzimegads azt jelzi printf-nek, hogy a megfelel argumentum egy hossz egsz (long integer). Ennl is nagyobb szmok esetn a double tpus (duplahosszsg lebegpontos szm) hasznlhat. A while helyett for utastst fogunk hasznlni,hogy bemutathassuk a ciklusszervezs egy msik lehetsgt. main() /*Megszmllja a bemeneten rkez karaktereket*/ { double nc; for (nc = 0; getchar() != EOF; ++nc) ; printf (%.0f \n, nc); } A printf mind float, mind double esetn elnyomja a nemltez trt rsz kirst. %f-et hasznl; a %.0f

kis

mdostsval

/* Megszmllja a bemeneten rkez karaktereket*/

A for ciklus trzse res, mivel az egsz feladat a felttelvizsgl s jrainicializl rszben hajtdik vgre. A C nyelvtani szablyai azonban megkvnjk, hogy a for utastsnak legyen trzse. Az egymagban ll pontosvessz, vagyis a nulla (res)utasts e kvetelmny kielgtse miatt szerepel. Kln sorba rtuk, hogy feltnbb legyen. Mieltt befejeznnk a karakterszmll program elemzst, felhvjuk a figyelmet, hogy ha a bemeneten nincsenek karakterek, akkor getchar

51

legels hvsakor a while vagy a for felttelvizsglata hamis rtket eredmnyez, s gy a program eredmnye elvrsunknak megfelelen 0 lesz. Ez lnyeges megfigyels. A while s a for egyik elnys tulajdonsga, hogy a felttelvizsglat a ciklus fejben van, megelzi a trzset. Ha teht semmit sem kell csinlni, akkor tnyleg semmi sem trtnik, mg akkor sem, ha emiatt a program sohasem halad t a ciklus trzsn. A programoknak akkor is rtelmesen kell mkdnik, ha a bemenet res. A while s a for segtsgvel a programok hatresetekben is sszeren viselkednek. Sorok szmllsa A kvetkez program megszmllja a bemenetre rkez sorokat. Felttelezzk, hogy a bemen sorok a \n jsor karakterrel fejezdnek be, amely szigoran minden kirt sor vgn megjelenik. main() /*A bemenetre rkez sorok szmllsa*/ { int c, nl ; nl = 0; while ((c = getchar()) != EOF) if (c == \n) ++nl; printf (%d\n, nl ); } A while trzse most egy if-et tartalmaz, amely pedig a ++ nl inkrementl mveletet vezrli. Az if utasts elvgzi a zrjelezett felttel vizsglatt, ha ennek eredmnye igaz, akkor vgrehajtja a rkvetkez utastst (vagy kapcsos zrjelek kztti utastscsoportot). A sorokat ismt gy rendeztk el, hogy vilgos legyen, mit mi vezrel. A C nyelv jellsmdjban az == (ketts egyenlsgjel) jelentse: egyenl . . .-vel (hasonlan a FORTRAN-beli .EO.-hoz). Ezzel a szimblummal klnbztetjk meg az egyenlsg vizsglatt a szimpla = jeltl, amit rtkadsra hasznlunk. Minthogy tipikus C programokban az rtkads krlbell ktszer olyan gyakran fordul el, mint az egyenlsgvizsglat, sszer, hogy az rtkad opertor fele olyan hossz legyen. Brmely egymagban ll karakter aposztrfok kz rva az illet karakternek a gp karakterkszletben szerepl numerikus rtkt jelenti: ezt karakterllandnak nevezzk. Igy pldul A karakterlland; az ASCII karakterkszletben ennek rtke 65, vagyis az A karakter bels brzolsa. Termszetesen knyelmesebb A-t rni, mint 65-t: A jelentse vilgos s fggetlen az adott karakterkszlettl. A karakterllandkban a karakterlncokban hasznlt escape jelsorozatok is megengedettek, gy a felttelvizsglatokban s aritmetikai kifejezsekben \n az jsor karakter kdrtkt jelenti. Ne feledjk, hogy \n egyetlen karakter, amely kifejezsekben egy egsz szmmal egyenrtk, \n viszont karakterlnc, amely az adott esetben egyetlen karaktert tartalmaz! A karakterek s karakterlncok tmjt a 2. fejezetben folytatjuk. 1.6. Gyakorlat. rjunk olyan programot, amely megszmllja a szkzket, tab s jsor karaktereket! 1.7. Gyakorlat. rjunk olyan programot, amely a bemenetet tmsolja a kimenetre, mikzben az egy vagy tbb szkzbl ll karakterlncokat egyetlen szkzzel helyettesti! 1.8. Gyakorlat. rjunk olyan programot, amely minden egyes tab karaktert a > , visszalptets (backspace), - hromkarakteres

52

sorozattal helyettest, ami thzott > -knt fog megjelenni, tovbb, amely a visszalptets karaktereket a hasonlan thzott < szimblummal helyettesti! Ezltal a tab karakterek s visszalptetsek lthatv vlnak. Szavak szmllsa Negyedik hasznos programunk sorokat, szavakat s karaktereket szmll annak a laza defincinak az alapjn, amely sznak tekint minden olyan karaktersorozatot, amely nem tartalmaz szkzt, tab vagy jsor karaktert. (Az albbi program az UNIX wc segdprogramjnak a vza.) #define YES 1 #define NO 0 main () /*A bemenet sorainak, szavainak, karaktereinek szmllsa*/ { int c, nl, nw, nc, inword; inword = NO; nl = nw = nc = 0; while ((c = getchar()) != EOF) { ++nc; if (c == \n) ++nl; if (c == ||c == \n ||c == \t) inword = NO; else if (inword == NO) { inword = YES; ++nw; } } printf (%d %d %d\n, nl, nw, nc); } Ahnyszor a program egy sz els karaktervel tallkozik, nveli a szmllt. Az inword vltoz jelzi, hogy a program pillanatnyilag egy szn bell van-e vagy sem; kezdetben nincs szn bell, miltal a NO rtk rendeldik hozz. Elnyben rszestjk a YES s NO szimbolikus llandkat az 1 s 0 szmrtkekkel szemben, mivel olvashatbb teszik a programot. Termszetesen egy ilyen kis programban, mint ez, ennek nemigen van jelentsge, de nagyobb programokban az rthetsg javulsa sokszorosan megri azt a szerny plusz fradsgot, ami, az ilyen stlus programrshoz szksges. Mdostani is knnyebb az olyan programot, ahol a szmok csupn szimbolikus llandknt jelennek meg. Az nl =nw=nc=0; sor mindhrom vltozt kinullzza. Ez nem specilis eset, hanem annak a tnynek a kvetkezmnye, hogy az rtkadsok jobbrl balra mennek vgbe. Ez valjban ugyanaz, mintha azt rtuk volna, hogy nc = (nl = (nw = 0));

53

A || opertor jelentse VAGY, teht az if (c == ||c == \n ||c == \t) sor azt jelenti, hogy ha c szkz vagy c jsor vagy c tab karakter . . . . (Mint mondottuk, a \t escape szekvencia a tab karakter lthat megjelensi formja.) Ltezik az ennek megfelel && opertor is az S kifejezsre. Az && vagy || opertorokkal sszekapcsolt kifejezsek kirtkelse balrl jobbra trtnik, s a kirtkels rgtn abbamarad, amint az egsz kifejezs igaz vagy hamis volta nyilvnvalv vlik. Ha teht c szkz karakter, nincs szksg annak megllaptsra, hogy c jsort vagy tabot tartalmaz-e, teht ezek a vizsglatok nem mennek vgbe. Itt most ez nem klnsen lnyeges, de bonyolultabb esetekben nagyonis fontos lehet, amint azt nemsokra ltni fogjuk. A pldban a C nyelv else utastsa is szerepel, amely megadja azt az alternatv tevkenysget, amit akkor kell elvgezni, ha az if felttelrsze hamis rtk. ltalnos alakja: if (kifejezs) 1.utasts else 2.utasts Az if-else-hez tartoz kt utasts kzl egy s csakis egy, mgpedig ha a kifejezs rtke igaz, akkor az 1. utasts, ha hamis, akkor a 2. utasts hajtdik vgre. Mindkt utasts valjban egszen bonyolult is lehet. A szavakat szmll programban pl. az else utni utasts egy jabb if, amely a kapcsos zrjelek kztti kt utastst vezrli. 1.9. Gyakorlat. Hogyan ellenrizhetjk a szavakat szmll programot? Mik lehetnek szhatrok. 1.10. Gyakorlat. Irjunk olyan programot, amely kln-kln sorokban nyomtatja ki a bemenetre rkez szavakat! 1.11. Gyakorlat. Mdostsuk a szavakat szmll programot gy, hogy jobban definiljuk a sz fogalmt, pldul a sz legyen betk, szmjegyek s aposztrfok olyan sorozata, amely betvel kezddik! 1.6. Tmbk rjunk olyan programot, amely megszmllja, hogy hnyszor fordulnak el az egyes szmjegyek, hny res helyet ad karakter (szkz, tab, jsor) s hny egyb karakter van a beolvasott llomnyban! Ez a feladat nyilvn mesterklt, de lehetv teszi, hogy egyetlen programban szemlltessk a C tbb jellegzetessgt. Tizenktfle bemeneti karaktert kell megklnbztetnnk, gy rdemes az egyes szmjegyek elfordulsainak szmt egy tmbben nyilvntartani ahelyett, hogy tz kln vltoznk lenne. A program egyik lehetsges vltozata: main() /*Szmjegyek, res helyek s egyb karakterek szmllsa*/

{ int c, i, nwhite, nother; int ndigit [10]; nwhite = nother = 0; for (i = 0; i < 10; ++i) ndigit [i] = 0; while ((c = getchar()) != EOF) if (c >= 0 && c <= 9) ++ ndigit [c - 0]; else if (c == || c == \n || c == \t) ++ nwhite; else

54

++nother; printf (szmjegyek=); for (i = 0; i < 10; ++i) printf (%d, ndigit [i]); printf (\n res hely = %d, egyb = %d\n, nwhite, nother); } Az int ndigit [10]; deklarci azt fejezi ki, hogy az ndigit egy 10 egszbl ll tmb. A tmbindexek a C nyelvben mindig 0-val kezddnek (s nem 1 -gyel, mint a FORTRAN-ban s a PL/1-ben), gy a tmb elemei: ndigit[0], ndigit [1], . . ., ndigit[9]. Ezt tkrzi a kt for ciklus: az egyik inicializlja, a msik kiratja a tmbt. Az index tetszleges egsz tpus kifejezs. gy termszetesen lehet az index egsz tpus vltoz, mint pl. i, valamint egsz rtk lland is. Az adott program lnyeges mdon kihasznlja a szmjegyek karakterbrzolsnak tulajdonsgait. Pldul az if (c >= 0 && c <= 9) . . . vizsglat eldnti, hogy a c-ben lev karakter szmjegy-e. Ha az, akkor az illet szmjegy numerikus rtke c - 0 Ez a mdszer csak akkor alkalmazhat, ha 0, 1, . . . nveked sorrend pozitv szmok s 0 s 9 kztt csak szmjegyek vannak. Szerencsre ez minden szoksos karakterkszlet esetben gy van. A char-okat s int-eket tartalmaz kifejezsekben definci szerint kirtkels eltt minden int-t konvertldik, gy a char vltozk s llandk aritmetikai szempontbl lnyegben az int mennyisgekkel azonosak. Ez egszen termszetes s knyelmes megolds: pldul c 0 egsz tpus kifejezs, amelynek rtke 0 s 9 kztt van a c-ben trolt 0 s 9 kztti karaktereknek megfelelen, s gy rvnyes indexe az ndigit tmbnek. Annak eldntse, hogy a karakter szmjegy, res hely vagy valami ms, az if (c >= 0 && c <= 9) ++ndigit [c - 0]; else if (c == || c == \n || c == \t) ++nwhite; else ++nother; programrsz segtsgvel trtnik. Az if (felttel) utasts else if (felttel)_ utasts else

55

utasts programszerkezetet elgazsok lersra.

gyakran

alkalmazzk

tbbutas

A programszveg beolvassa fellrl kezdve mindaddig folytatdik, amg a gp igaz felttelt nem tall. Ekkor vgrehajtja az odatartoz utasts rszt, s az egsz mvelet vgetr. (Az utasts termszetesen tbb, kapcsos zrjelek kz zrt utasts is lehet.) Ha egyik felttel sem igaz, a gp az utols else utni utastst hajtja vgre, amennyiben van ilyen. Ha az utols else s a hozztartoz utasts hinyzik (mint a szavakat szmll programban), semmi sem trtnik. A kezd if, valamint a zr else kztt tetszleges szm else if (felttel) utasts csoport fordulhat el. Stilris szempontbl clszer a bemutatott mdon megszerkeszteni ezt a programrszt, hogy a hossz dntsi lncok ne nyljanak tl a papr jobb szln. A 3. fejezetben fogunk szlni a switch utastsrl, amely szintn tbbutas programelgaztatsok lersra ad lehetsget. A switch alkalmazsa klnsen akkor elnys, amikor azt vizsgljuk, hogy egy adott egsz vagy karakter tpus kifejezs rtke egyenl-e egy llandkbl ll halmaz valamelyik elemvel. Az sszehasonlts cljbl a 3. fejezetben bemutatjuk az elbbi a program switch utastssal megrt vltozatt. 1.12. Gyakorlat. rjunk olyan programot, amely kinyomtatja a bemenetn elfordul szavak hosszsgnak hisztogramjt! A legegyszerbb, ha a hisztogramot vzszintesen rajzoljuk; a fggleges irny rajzols nehezebb feladat. 1.7. Fggvnyek A C nyelvben a fggvny ugyanaz, mint a FORTRAN-ban a szubrutin, ill. fggvny, vagy a PL/1-ben, a PASCAL-ban s ms nyelvekben az eljrs. A fggvny knyelmes lehetsget nyjt szmunkra, hogy valamely szmtsi rszt fekete dobozba zrjunk, amelyet azutn hasznlhatunk anlkl, hogy tartalmval trdnnk kellene. Valjban csak a fggvnyek segtsgvel brkzhatunk meg nagy s bonyolult programokkal. Helyesen tervezett fggvnyek esetben teljesen figyelmen kvl hagyhatjuk, hogyan keletkezik a fggvny rtke (eredmnye); elegend a feladat s az eredmny ismerete. A C nyelv egyszer, knyelmes s hatkony fggvnyhasznlatot tesz lehetv. Gyakran fogunk olyan fggvnyekkel tallkozni, amelyek csupn nhny sorbl llnak s amelyeket csak egyszer hvunk meg: ezeket kizrlag a program vilgosabb ttele rdekben hasznljuk. Ezidig csak olyan fggvnyeket hasznltunk, mint a printf, a getchar vagy a putchar, amelyeket kszen kaptunk; itt az ideje, hogy magunk is rjunk nhnyat. Mivel a C nyelvnek nincs olyan hatvnyoz opertora, mint a ** a FORTRAN-ban vagy a PL/1-ben, szemlltessk a fggvnydefinils technikjt a power(m, n) fggvny megrsval, amely az m egsz tpus vltozt a pozitv egsz n hatvnyra emeli. Teht a power(2,5) fggvny rtke 32. Ez a fggvny nyilvnvalan nem tudja mindazt, amit ** tud, mivel csak kis egsz szmok pozitv hatvnyait tudja kezelni, de legjobb, ha egyszerre csak egy problmra sszpontostunk. Az albbiakban a power fggvnyt egy fprogramba gyazva mutatjuk be. Ne feledjk, hogy a main() maga is fggvny!

56

main ()

/*Hatvnyoz fggvny tesztelse*/

{ int i; for (i = 0; i < 10; ++i) printf (%d %d %d\n, i, power (2,i), power (-3,i)); } power (x,n) /*x n-dik hatvnyra emelse; n >0*/ int x, n; { int i, p; p = 1; for (i = 1; i <= n; ++i) p=p*x; return (p); } Mindkt fggvny az albbi alak: nv (opcionlis argumentumlista) opcionlis argumentumdeklarcik { deklarcik utastsok } A fggvnyek tetszleges sorrendben szerepelhetnek, s egy vagy kt forrsllomnyban egyarnt llhatnak. Termszetesen, ha a forrs kt llomnyban tallhat, bonyolultabb a fordts s a tlts, mintha minden egyetlen llomnyban van, de ez az opercis rendszer krdse s nem a nyelvjellegzetessge. Pillanatnyilag feltesszk, hogy a kt fggvny ugyanabban az llomnyban van, teht mindaz, amit a C programok futtatsrl megtanultunk, nemvltozik. A power fggvnyt a printf (%d %d %d\n, i, power(2,i), power(-3,i)); sorban ktszer hvtuk meg. Mindkt hvs kt argumentumot ad t a power fggvnynek, amely mindkt alkalommal visszaad egy-egy egsz szmot, amit a hv program formtumoz s megjelent. Kifejezsen bell power(2, i) ugyanolyan egsz, mint 2 s i. (Nem minden fggvny eredmnyez egsz rtket: ezt a tmt a 4. fejezetben folytatjuk.) A power argumentumait megfelelkppen deklarlni kell ahhoz, hogy a tpusuk ismert legyen. Ez a fggvny nevt kvet int x, n; sorban trtnik. Az argumentumdeklarcik az argumentumlista s a nyit bal kapcsos zrjel kztt vannak; minden deklarcit pontosvessz zr le. A power fggvny ltal a sajt argumentumai szmra hasznlt nevek teljes mrtkben loklisak a power fggvnyre nzve, azokhoz semmilyen ms fggvny sem frhet hozz: ms rutinok veszlytelenl hasznlhatjk ugyanezeket a neveket. Ez a p s az i vltozra is vonatkozik: a power-beli i vltoznak semmi kze a main-ben hasznlt i-hez. A power fggvny ltal kiszmtott rtket - mint a PL/1-ben - a return utasts adja vissza a main-nek. A zrjelek kztt tetszleges kifejezs elfordulhat. Egy fggvnynek nem felttlenl szksges rtket visszaadnia: egy kifejezs nlkli return utasts tadja a vezrlst, de nem ad t hasznos rtket a hvnak - ez trtnik olyankor, amikor a vezrls a fggvny vgt tlpi azltal, hogy elri a jobb oldali zr kapcsos zrjelet.

57

1.13. Gyakorlat. Irjunk olyan programot, amely a beolvasott szveget kisbetss alaktja t egy olyan lower fggvny segtsgvel, amely c-vel tr vissza, ha c nem bet, s c kisbets megfeleljt adja vissza, ha c bet! D 1.8. Argumentumok; rtk szerinti hvs A C fggvnyek egyik tulajdonsgt ms nyelvekben klnsen a FORTRAN-ban vagy PL/1-ben - jratos programozk szokatlannak tallhatjk: a C-ben mindig rtk szerinti fggvnyargumentum-tads trtnik. Ez azt jelenti, hogy a hvott fggvny az argumentumainak nem a cmt, hanem - ideiglenes vltozban (valjban egy veremben) az rtkt kapja meg. Ez bizonyos eltr tulajdonsgokhoz vezet az olyan nv szerint hv nyelvekhez kpest, mint amilyen a FORTRAN s a PL/1, amelyekben a hvott rutin az argumentum cmt, nem pedig az rtkt kapja meg. A f klnbsg az, hogy a C-ben a hvott fggvny nem tudja megvltoztatni a hv fggvny vltozinak rtkt, csak a sajt, ideiglenes vltozpldnyainak tud j rtket adni. Az rtk szerinti hvs azonban elny, nem pedig htrny. ltala legtbbszr tmrebb programokat llthatunk el, kevesebb segdvltozt kell hasznlnunk, mivel a hvott rutinban az argumentumok ugyanolyan mdon kezelhetk, mint a hagyomnyosan inicializlt vltozk. Nzzk pldul a power kvetkez vltozatt, amely kihasznlja ezt a tnyt: power (x,n) 2. vltozat*/ int x, n; { int p; for (p = 1; n > 0; --n) p = p * x; return (p); } Az n argumentumot ideiglenes vltozknt hasznltuk s addig dekrementltuk, amg el nem rte a 0-t; gy nincs szksg az i vltozra. Mindannak, ami az n-nel a power-en bell trtnik, nincs befolysa arra az argumentumra, amellyel eredetileg a fggvnyt meghvtuk. Szksg esetn megoldhat, hogy a fggvny mdostani tudja az t hv rutin valamelyik vltozjt. A hvnak meg kell adnia a mdostand vltoz cmt (gyakorlatilag egy, a vltozt megcmz mutatt), s a hvott fggvnynek az argumentumot mutatknt kell deklarlnia, a tnyleges vltozra ezen keresztl, indirekt mdon kell hivatkoznia. Ezzel az 5. fejezetben foglalkozunk. Ha egy tmb nevt hasznljuk argumentumknt, akkor a fggvnynek tadott rtk tnylegesen a tmb kezdetnek helye vagy cme. (A tmbelemek nem msoldnak t). Ezt az rtket indexelve a fggvny a tmb tetszleges elemt elrheti s megvltoztathatja. Ezzel a kvetkez fejezet foglalkozik. 1.9. Karaktertmbk A C nyelvben leggyakoribb tmbtpus valsznleg a karaktertmb. A karaktertmbk s az ket kezel fggvnyek hasznlatt egy olyan programmal szemlltetjk, amely sorokat olvas be s kzlk a leghosszabbat megjelenti. Az alapstruktra meglehetsen egyszer : /*x n-edik hatvnyra emelse; n > 0;

58

while (van mg sor) if (hosszabb, mint az eddigi leghosszabb sor) trold a sort s a hosszt nyomtasd ki a leghosszabb sort Ez a struktra vilgoss teszi a program termszetes tagozdst. Az egyik rsz beolvassa s megvizsglja az jsort, a msik trolja, a harmadik vezrli a folyamatot. Minthogy a feladatok ilyen szpen elklnthetk, helyes, ha a programot is eszerint rjuk meg. Ennek megfelelen elszr rjunk egy kln getline fggvnyt, amely beolvassa a bemenetrl a kvetkez sort,_ ez a getchar fggvny ltalnostsa. Szeretnnk ha a fggvny ms krnyezetben is hasznlhat lenne, ezrt igyeksznk a lehet legrugalmasabb tenni. A minimlis igny, hogy a getline jelezze vissza az esetleges llomnyvget; ltalnosabban hasznlhat lesz a fggvny, ha a sor hosszt adja vissza, vagy pedig nullt, ha elrte az llomny vgt. A nulla bizonyosan nem valdi sorhossz, mivel minden sor legalbb egy karaktert kell, hogy tartalmazzon, mg a csupn egyetlen soremelst tartalmaz sor hossza is 1. Ha azt talljuk, hogy egy sor hosszabb, mint az addigi leghosszabb sor, valahov el kell mentennk. Logikus, hogy ez egy msodik fggvny, a copy feladata legyen, amely az j sort biztos helyre menti. Vgezetl szksgnk van egy fprogramra, amely vezrli a getline-t s a copy-t. Ime az egsz program: #define MAXLINE 1000 /*A beolvasott sor maximlis mrete*/ main () /*A leghosszabb sor kivlasztsa*/ { int len; /*A pillanatnyi sor hossza*/ int max; / *Az eddigi maximlis hossz*/ char line [MAXLINE]; /*A pillanatnyilag olvasott sor*/ char save [MAXLINE]; /*A leghosszabb sor mentsre*/ max = 0; while ((len = getline (line,MAXLINE)) > 0) if (len > max) { max = len; copy (line,save); } if (max > 0) /*Volt sor*/ printf (%s, save); } getline (s,lim) /*Sor beolvassa s-be, a hosszt adja vissza*/ char s []; int lim; { int c, i; for (i = 0; i < lim - 1 && (c =getchar ()) != EOF && c != \n; ++i) s [i] = c; if (c == \n) { s [i] = c; ++i; } s [i] = \0; return (i); }

59

/*s1 msolsa s2-be; s2-t elg nagynak felttelezi*/ char s1 [], s2 []; { int i; i = 0; while ((s2 [i] = s1 [i]) != \0) ++i; } main csakgy, mint getline, kt argumentumon s egy visszaadott rtken keresztl kommunikl. A getline-ban az argumentumokat a char s []; int lim; sorok deklarljk, amelyek elrjk, hogy az els argumentum tmb, a msodik pedig egsz tpus legyen. Az s tmb hossza getline-ban nincs megadva, mivel azt a main-ben hatrozzuk meg. A getline a return utasts segtsgvel kld vissza rtket a hvnak gy, ahogy azt a power fggvnynl lttuk. Egyes fggvnyekhasznos rtket szolgltatnak, mg msokkal, gy a copy-val is valamely adott feladatot vgeztetnk el, s nem adnak vissza hasznos rtket. A getline az ltala ltrehozott tmb vgre, a karakterlnc vgnek jelzsre egy \0 karaktert (egy nulla karaktert, amelynek rtke 0) helyez el. gy mkdik a C fordt is: amikor egy karakterlnc lland, mint pldul hello\n van a C programban, a fordt a lnc karaktereit tartalmaz karaktertmbt hoz ltre, amelyet egy \0-val zr le. A fggvnyek, pl. a printf, gy kpesek a karakterlnc vgnek az rzkelsre: h e l l o \n \0 A printf %s formtumspecifikcija egy ilyen formban brzolt karakterlncot vr. Ha megvizsgljuk a copy fggvnyt, szrevehetjk, hogy az is arra tmaszkodik, hogy az s1 bemeneti argumentumot \0 zrja le, s ezt a karaktert is tmsolja az s2 kimeneti argumentumra. (Mindez azt felttelezi, hogy \0 nem rsze a norml szvegnek.) Futlag rdemes megjegyeznnk, hogy mg egy ilyen kis program is felvet nhny knyes tervezsi problmt. Pldul mit csinljon main, ha olyan sorral tallkozik, amely hosszabb, mint a megadott korlt? A getline helyesen mkdik: amikor a tmb megtelt, lell, mg akkor is, ha nem tallt jsort. A hosszat s az utolsnak visszaadott karaktert ellenrizve a main el tudja dnteni, hogy a sor tl hossz volt-e, majd tetszs szerint cselekedhet. A rvidsg kedvrt ezt a problmt figyelmen kvl hagytuk. Aki a getline fggvnyt hasznlja, nem tudhatja elre, hogy milyen hossz lehet egy beolvasott sor, gy a getline ellenrzi a tlcsordulst. Msfell a copy hasznlja mr tudja (vagy kidertheti), hogy mekkork a karakterlncok, ezrt gy dntttnk, hogy ezt a fggvnyt nem egsztjk ki hibaellenrzssel. 1.14. Gyakorlat. Mdostsuk a leghosszabb sort keres program f rutinjt oly mdon, hogy helyesen rja ki tetszlegesen hossz bemeneti sorok hosszt s a szvegbl annyit, amennyi csak lehetsges! 1.15. Gyakorlat. Irjunk programot, amely minden olyan sort megjelent, amely 80 karakternl hosszabb!

copy (s1,s2)

60

1.16. Gyakorlat. Irjunk olyan programot, amely eltvoltja a sorvgi szkzket s tab karaktereket a bemenet minden sorbl s trli a teljesen res sorokat! 1.17. Gyakorlat.Irjunk olyan reverse(s) fggvnyt, amely megfordtja az s karakterlncot! Hasznljuk fel ezt a fggvnyt olyan program megrshoz, amely soronknt megfordtja a beolvasott szveget! 1.10. rvnyessgi tartomny; kls vltozk A main-en belli vltozk (line, save stb.) a main sajt vltozi, vagyis a main-re nzve loklisak. Mivel ezeket a main-en bell deklarltuk, egyetlen ms fggvny sem tud kzvetlenl hozzjuk frni. Ugyanez mondhat ms fggvnyek vltozirl; pldul a getline fggvnyen belli i vltoz fggetlen a copy i vltozjtl. A fggvnyek loklis vltozi csak meghvsukkor jnnek ltre, s megsemmislnek, amikor a vezrls a fggvnybl kilp. Az ilyen dinamikus loklis vltozkat ezrt - ms nyelvek szhasznlathoz hasonlan - automatikus vltozknak nevezzk. A 4. fejezetben trgyaljuk az n. static trolsi osztlyt, amelyben a loklis vltozk megtartjk az rtkket kt fggvnyhvs kztt. Minthogy az automatikus vltozk lettartama arra az idre korltozdik, amg a vezrls a fggvnyen van, rtkket nem rzik meg egyik hvstl a msikig, gy minden belpskor explicit mdon rtket kell adni nekik. Ha ezt elmulasztjuk, tartalmuk bizonytalan. Az automatikus vltozk mellett olyan vltozkat is definilhatunk, amelyek az sszes fggvnyre nzve klsk, gy rtkk fggvnyhvsoktl fggetlenl fennmarad. Ezeket a globlis vltozkat brmelyik fggvny nv szerint elrheti (hasonlan a FORTRAN nyelv common vagy a PL/1 external mechanizmushoz). Globlis hozzfrhetsgk miatt a fggvnyek kztti adattadst argumentumlistk helyett kls vltozkon keresztl is megoldhatjuk. A kls vltozkat az sszes fggvnyen kvl kell definilni: ezzel trolhelyet foglalunk le szmukra. A vltozkat minden olyan fggvnyben, ahol hasznlni akarjuk, vagy explicit mdon az extern alapszval, vagy implicit mdon rtelemszeren, de deklarlnunk is kell. Mindez bizonyra rthetbb lesz, ha pldaknt jra megrjuk a leghosszabb sort keres programot gy, hogy a line, a save s a max kls vltoz legyen. Ehhez mindhromfggvnyben meg kell vltoztatnunk a hvsokat, a deklarcikat s a fggvnyek trzseit. #define MAXLINE 1000 /*A beolvasott sor maximlis mrete*/ char line [MAXLINE]; /* A beolvasott sor*/ char save [MAXLINE]; /*A leghosszabb sor mentsre*/ int max; /*Az eddigi maximlis hossz*/ main () /*A leghosszabb sor specilis vltozat*/ kivlasztsa;

{ int len; /*A pillanatnyi sor hossza*/ extern int max; extern char save [ ]; max = 0; while ((len = getline ()) > 0) if (len > max) {

61

max = len; copy (); } if (max > 0) /*Volt sor*/ printf (%s, save); } getline () /* Specilis vltozat*/

{ int c, i; extern char line []; for (i = 0; i < MAXLINE - 1 && (c=getchar ()) != EOF && c != \n; ++i) line (i] = c; if (c == \n) { line [i] = c; ++i; } line [i] = \0; return (i); } copy () /*Specilis vltozat*/

{ int i; extern char line [], save []; i = 0; while ((save [i] = line [i]) != \0) ++i; } Pldnkban a main, a getline s a copy fggvnyben elfordul kls vltozkat az els sorokban definiltuk, itt hatroztuk meg tpusukat s foglaltuk le a szksges trterletet. Ezek a kls defincik ugyanolyan felptsek, mint a korbban ltott deklarcik, de mivel fggvnyeken kvl fordulnak el; kls vltozkat adnak meg. Fggvnyben kls vltozt csak akkor hasznlhatunk, ha elzleg kzljk a fggvnnyel a vltoz nevt. Ennek egyik mdja, hogy a fggvnyben egy extern deklarcit helyeznk el, amely mindssze abban klnbzik az eddigi deklarciktl, hogy az extern alapszval kezddik. Bizonyos krlmnyek kztt az extern deklarci elhagyhat; ha a forrsszvegben egy vltoz kls defincija megelzi a vltoz hasznlatt valamely fggvnyben, akkor e fggvnyben nincs szksg extern deklarcira. Igy a main, a getline s a copy fggvnyben az extern deklarcik feleslegesek. Gyakorlott C-programozk ltalban a forrsszveg elejn definiljk az sszes kls vltozt, s nem hasznlnak extern deklarcikat. Ktelez azonban az extern deklarci, ha forrsprogramunk tbb llomnyra tagoldik, s egy vltozt, mondjuk az A llomnyban definilunk, de B-ben hasznlunk, hiszen ilyenkor a vltoz kt elfordulsa kztt csak a B-ben elhelyezett extern deklarcival teremthetnk kapcsolatot. Ezt a tmt bvebben a 4. fejezetben fejtjk ki. Nem szabad sszetvesztennk a kls vltozk deklarcijt s defincijt! A definci az a programsor, ahol a vltozt tnylegesen ltrehozzuk, szmra trhelyet foglalunk le; a

62

deklarci viszont olyan programrsz, ahol csupn lerjuk a vltoz tulajdonsgait, de trhelyfoglals nem trtnik. Megjegyezzk, hogy az ember hajlamos az gvilgon mindent kls vltozknt megadni, mivel az ltszlag egyszersti az adattadst - az argumentumlistk rvidek, s a vltozk mindig rendelkezsre llnak, amikor csak akarjuk. Csakhogy a kls vltozk akkor is ott vannak, ha nem akarjuk! Ez a programozsi stlus slyos veszlyeket hord magban. Az gy rt programokban az adattadsok ttekinthetetlenek - a vltozk vratlanul, st a programoz szndktl teljesen eltr mdon megvltozhatnak, s a program igen nehezen mdosthat. Emiatt a leghosszabb sort keres program msodik vltozata gyengbb az elsnl, de hibja az is, hogy a vltozk nevnek rgztsvel kt hasznos fggvny elveszti ltalnos jellegt. 1.18. Gyakorlat. Az elbbi getline fggvny for utastsban a felttelvizsglat meglehetsen gyetlen. Javtsunk rajta, de gy, hogy az llomny vgn vagy puffertlcsordulskor a program az eddigi mdon mkdjn! Biztos, hogy ez a legjobb szervezs? 1.11. sszefoglals Az 1. fejezetben ttekintettk a C nyelv legfontosabb elemeit. Ebbl a nhny ptelembl is tekintlyes mret, hasznos programokat rhatunk, s valsznleg j gondolat, ha ennek rdekben az olvas most megfelel sznetet tart a knyv olvassban. Az albb kvetkez gyakorlatokban programtleteket szeretnnk adni olyan programokra, amelyek bonyolultabbak mint azok, amelyeket ez a fejezet bemutatott. Ha az olvas mr elsajttotta a C nyelv eddig ismertetett elemeit, folytassa az olvasst, mivel a kvetkez nhny fejezetben olyan jellegzetessgekrl szlunk, amelyek nagyban hozzjrulnak a nyelv erejhez s kifejezkpessghez. 1.19. Gyakorlat. Irjunk detab nven programot, amely a bemeneten tallt tab karakterek mindegyikt annyi szkzzel helyettesti, amennyi a kvetkez tabultorstop-ig htravan! Ttelezznk fel egy rgztett tabultorstopkszletet, a stop-ok mondjuk minden nedik pozcin tallhatk. 1.20. Gyakorlat. Irjuk meg az entab programot, amely a szkzkbl ll karakterlncok helybe a minimlis szm tab karaktert s szkzt rja gy, hogy a tvolsg ne vltozzon! Hasznljuk ugyanazokat a tab stop-okat, mint a detab-nl! 1.21. Gyakorlat. Irjunk olyan programot, amely a sor n-edik pozcija eltt elfordul utols, nem szkz karakter utn sszehajtjaa hossz bemeneti sorokat (n paramter)! Gyzdjnk meg rla, hogy a program tnyleg rtelmesen mkdik nagyon hossz sorok esetn, de akkor is, ha a megadott pozci eltt egyltaln nem szerepel szkz vagy tab! 1.22. Gyakorlat. Irjunk olyan programot, amely egy C programbl az sszes megjegyzst eltnteti! Ne felejtkezznk meg az idzjelezett karakterlncok s karakterllandk helyes kezelsrl! 1.23. Gyakorlat. Irjunk olyan programot, amely a C programban megtallja az olyan alapvet szintaktikai hibkat, mint a nem azonos szm nyit s zr kerek, szgletes, ill. kapcsos zrjelek! Ne felejtkezznk meg az aposztrfokrl, idzjelekrl, valamint a megjegyzsekrl sem ! (Ezt a programot teljes ltalnossgban nehz elkszteni.) _

63

2.

fejezet: Tpusok, opertorok s kifejezsek

A programok alapvet adatobjektumai a vltozk s az llandk. A deklarcik felsoroljk a hasznlni kvnt vltozkat, kzlik a tpusukat, valamint az esetleges kezdeti rtkket. Az opertorok azt hatrozzk meg, hogy mit kell tenni a vltozkkal. A kifejezsek a vltozkbl s llandkbl j rtkeket hoznak ltre. Fejezetnkben ezekkel foglalkozunk. 2.1. Vltoznevek Br eddig errl nem beszltnk, a vltozk s szimbolikus llandk neveire nzve vannak bizonyos megktsek. A nevek betkbl s szmjegyekbl llnak: az els karakter bet kell, hogy legyen. Az alhzs karakter (_) betnek szmt: ezzel javthatjuk a hossz vltoznevek olvashatsgt. A nagy- s a kisbet klnbznek szmt; a hagyomnyos C programozsi gyakorlat szerint a vltoznevek kisbetsek, a szimbolikus llandk csupa nagybetbl llnak. A bels nevekben csupn az els nyolc karakter rtkes, br ennl hoszszabb nevek is hasznlhatk. Kls nevek esetn, gy fggvnyneveknl s kls vltozknl ez a szm nyolcnl kevesebb is lehet, mivel a kls neveket klnfle assemblerek s tltprogramok (loaderek) is hasznljk. Ennek rszleteit az A fggelk ismerteti. Ezenkvl az olyan kulcsszavak, mint if, else, int, float stb. fenntartott szavak; vltoznvknt nem hasznlhatk. (Kisbetseknek kell lennik.) Termszetesen sszer olyan vltozneveket vlasztani, amelyek jelentenek valamit, kapcsoldnak a vltoz funkcijhoz, s tipogrfiailag nem zavark. 2.2. Adattpusok s mretek A C-ben csak nhny alapvet adattpus van: char egyetlen byte, amely az rvnyes karakterkszlet egy elemt tartalmazhatja. int egsz szm, amely tipikusan a befogad gpre jellemz egsz szm brzolsi mretet tkrzi. float egyszeres pontossg lebegpontos szm. double ktszeres pontossg lebegpontos szm. Ezen kvl van nhny minst szimblum, amely az int mennyisgekre alkalmazhat: short, long, valamint unsigned. short (rvid), ill. long (hossz) klnbz mret egsz szmot jell. Az unsigned (eljel nlkli) szmokra a modulo 2n aritmetika szablyai vonatkoznak, ahol n az int tpust brzol bit-ek szma; az unsigned szmok mindig pozitvak. A minstk deklarcijnak alakja: short int x; long int y; unsigned int z; Ilyen esetekben az int sz elhagyhat, s el is szoks hagyni. Ezeknek az objektumoknak a pontossga a rendelkezsre ll gptl fgg; a kvetkez tblzat nhny - bitekben megadott - jellemz rtketmutat. DEC PDP-11 ASCII char 8 int 16 short 16 long 32 float 32 double 64 Honeywell6000 IBM 370 Interdata 8/32 ASCII EBCDIC ASCII 8 8 8 36 32 32 36 16 16 36 32 32 36 32 32 72 64 64

64

A cl az, hogy ahol kvnatos, a short, ill. a long klnbz hosszsg egszeket hozzon ltre; int ltalban az adott gpnek megfelel legtermszetesebb mret. Lthat, hogy minden fordt a sajt hardverjnek megfelelen szabadon rtelmezheti a short, ill. long minstket, az azonban bizonyos, hogy a short nem hosszabb, mint a long. 2.3. llandk Az int s float llandkkal mr vgeztnk, csupn azt tesszk mg hozz, hogy a szoksos 123.456e-7 vagy a 0. 123E3 jellsmd a float szmok esetben egyarnt megengedett. Minden lebegpontos lland double-nak szmt, ezrt az e jells a float s a double szmokra egyarnt megfelel. A long llandk rsmdja: 123L. Azok a kznsges egsz llandk, amelyek hosszabbak annl, hogy egy int-be belefrjenek, ugyancsak long-nak szmtanak. Kln jellsmdja van az oktlis s a hexadecimlis llandknak:ha egy int tpus lland 0-val (nullval) kezddik, a szm nyolcas (oktlis) szmrendszerben rtend; a vezet 0x vagy 0X pedig azt jelenti, hogy hexadecimlis (tizenhatos szmrendszerbeli) szmrl van sz. Pldul a decimlis 31 ugyanannyi, mint az oktlis 037 vagy a hexadecimlis 0x1F, ill. 0X1F. A hexadecimlis s oktlis llandkbl az utnuk rt L-lel szintn kpezhetnk long mennyisget. A karakterlland egyetlen, aposztrfok kz rt karakter, pldul x. A karakterlland rtke a karakternek a gp karakterkszletn belli numerikus rtke. Pldul a nulla karakter, vagyis 0 rtke az ASCII karakterkszletben 48, az EBCDIC-ben pedig 240, mindkt rtk teljesen klnbz a 0 numerikus rtktl. Ha szmrtkek, mint 48 vagy 240 helyett 0-t runk, akkor a program fggetlenn vlik a karakter adott rtktl. A karakterllandk ugyangy vesznek rszt a numerikus mveletekben, mint brmilyen ms szm, br leggyakrabban ms karakterekkel val sszehasonltsra hasznljuk ket. A konverzis szablyokkal egy ksbbi fejezet foglalkozik. Bizonyos nemgrafikus karakterek escape szekvencik segtsgvel brzolhatk karakterllandknt, mint pldul \n (jsor), \t (tab), \0 (nulla), \\(fordtott trtvonal), \ (aposztrf) stb., amelyek kt karakternek ltszanak, de valjban mindegyik csak egy karakter. Ezenkvl tetszleges, egy byte mret bit-minta hozhat ltre a \ddd alak segtsgvel, ahol ddd egy, kett vagy hrom oktlis szmjegy, pl.: #define FORMFEED \014 /* ASCII lapdobs karakter*/ A \0 karakterlland a nulla rtk karaktert jelli. 0 helyett gyakran runk \0-t, amivel valamely kifejezs karakter jellegt hangslyozzuk. Az lland kifejezs olyan kifejezs, amely csak llandkat tartalmaz. Az ilyen kifejezsek kirtkelse fordtsi idben

65

trtnik, nem pedig futsi felelnek meg. Pldul: #define MAXLINE 1000 char line [MAXLINE + 1]; vagy seconds = 60 * 60 * hours;

idben,

gy

egyszer

llandnak

A karakterlnc-lland (stringkonstans) idzjelek kz zrt, nulla vagy tbb karakterbl ll sorozat, pl. ez itt egy karakterlnc vagy /* res karakterlnc*/ Az idzjelek nem rszei a karakterlncnak, csupn annak hatrolsra szolglnak. A karakterlncokban ugyanazok az escape szekvencik hasznlhatk, mint amelyeket a karakterllandknl lttunk; \ az idzjel karaktert jelli. Gyakorlatilag a karakterlnc olyan tmb, amelynek minden eleme egy-egy karakter. A fordt automatikusan elhelyezi a \0 nullakaraktert minden ilyen karakterlnc vgre, gy a programok knyelmesen megtallhatjk a karakterlnc vgt. Ez a fajta brzols azt jelenti, hogy nincs tnyleges hatra a karakterlnc hossznak, de egy adott karakterlnc hossznak megllaptshoz a programnak vgig kell mennie az illet karakterlncon. A szksges fizikai trhely nagysga egy trhellyel tbb, mint az idzjelek kz rt karakterek szma. Az albbi strlen(s) fggvny az s karakterlnc hosszt adja vissza, kizrva ebbl a zr \0-t. strlen (s) /* s hossznak kiszmtsa*/ char s []; { int i; i = 0; while (s [i] != \0) ++i; return (i); } Vigyzat! A karakterlland s az egyetlen karaktert tartalmaz karakterlnc kt klnbz dolog: x nem ugyanaz, mint x. Az elbbi egyetlen karakter, amely az x betnek a gp karakterkszlete szerint megfelel szmrtk ellltsra szolgl, az utbbi egy karakterlnc, amely egy karaktert (az x bett) s egy \0-t tartalmaz. 2.4. Deklarcik Hasznlat eltt minden vltozt deklarlni kell, br bizonyos deklarcik implicit mdon, rtelemszeren keletkeznek. A deklarci meghatroz egy tpust, amelyet az illet tpus vltoz(ka)t megad lista kvet, mint pldul: int lower, upper, step; char c, line [1000]; A vltozk tetszleges mdon oszthatk szt a deklarcik kztt; az elz listkat gy is rhattuk volna: int lower; int upper; int step; char c; char line [1000];

66

az utbbi forma tbb helyet ignyel, de gy pl. minden deklarcihoz vagy az azt kvet mdostsokhoz megjegyzst fzhetnk. A vltozk sajt deklarciikban inicializlhatk is, br ezzel kapcsolatban vannak megktsek. Ha a nevet egy egyenlsgjel s egy lland kveti, akkor az az illet vltoz kezdeti rtknek megadst (inicializlst) jelenti: char backslash = \\; int i = 0; float eps = 1.0e-5; Kls vagy statikus vltoz esetn az inicializls csak egyszer rtelemszeren a program vgrehajtsnak megkezdse eltt trtnik meg. Az explicit mdon inicializlt automatikus vltozk minden alkalommal inicializldnak, amikor az ket tartalmaz fggvnyt egy program meghvja. Az explicit hatrozatlan. inicializls nlkli automatikus vltozk rtke

A kls s statikus vltozk kezdeti rtke alaprtelmezs szerint nulla, de stilrisan helyesebb, ha minden esetben megadjuk a kezdeti rtket. Az inicializls tmjt adattpusokrl lesz sz. akkor folytatjuk, amikor a tovbbi

2.5. Aritmetikai opertorok Az aritmetikai opertorok a +, -, *, / s a % (modul) opertor. Van egyoperandus -, de nincs egyoperandus +. Az egsz tpus (integer) oszts levgja a trt rszt. Az x % y kifejezs az x-nek y-nal trtn osztsakor keletkez maradkot jelenti, teht rtke nulla, ha x pontosan oszthat y-nal. Pldul egy v ltalban akkor szkv, ha az vszm 4-gyel oszthat, de nem oszthat 100-zal. Kivtelt jelentenek a 400-zal oszthat vek, amelyek szintn szkvek. gy if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0) szkv van else nincs szkv A % opertor float s double mennyisgekre nem alkalmazhat. A + s - opertorok precedencija azonos s alacsonyabb, mint a * , / s % (egymssal szintn azonos) precedencija, amely viszont alacsonyabb, mint az egyoperandus mnusz. Az aritmetikai opertorok balrl jobbra ktnek. (A 2. fejezet vgn kzlt tblzat sszefoglalja az sszes opertor precedencijt s ktsi mdjt.) A kirtkels sorrendje olyan asszociatv s kommutatv opertoroknl, mint a s +, nincs meghatrozva; a fordt trendezheti az olyan zrjelezett szmtsokat, amelyek ezek valamelyikt tartalmazzk. gy a+(b+c) azonos (a+b)+c-vel. Ennek ritkn van jelentsge, de ha adott sorrendre van szksg, akkor explicit ideiglenes vltozkat hasznlhatunk. A tlcsorduls s alulcsorduls esetnek kezelse az adott gptl fgg.

67

2.6.

Relcis s logikai opertorok

A relcis opertorok: > >= < <= = Ezek mindegyiknek azonos a precedencija. Eggyel alacsonyabb - s egyms kzt egyez - precedencijak az egyenlsgopertorok: == != A relcis opertorok precedencija alacsonyabb, mint az aritmetikaiak, gy a vrakozsnak megfelelen i < lim - 1 ugyanaz, mint i < (lim - 1). Mg rdekesebbek a && s || logikai sszekapcsol mveletek. A && vagy || szimblumokkal sszekapcsolt kifejezsek kirtkelse balrl jobbra trtnik, s a kirtkels azonnal megll, amint az eredmny igaz vagy hamis volta kiderl. Ezek a tulajdonsgok lnyegbevgak, ha jl mkd programokat akarunk rni. Itt van pldul az 1. fejezetben rt getline sorbeolvas fggvny egyik ciklusa: for (i = 0; i < lim - 1 && (c = getchar()) != \n && c != EOF; ++i) s [i] = c; j karakter beolvassa eltt nyilvnvalan ellenriznnk kell, hogy a beolvasand karakter trolshoz van-e elg hely az s tmbben, gy az i < lim - 1 vizsglatot kell elsknt vgrehajtani! St, ha a felttel nem ll fenn, jabb karaktert mr nem szabad beolvasni! Ugyancsak nem volna szerencss, ha a c-nek az EOF-fal trtn sszehasonltsa a getchar hvsa eltt trtnne meg_ a hvsnak meg kell elznie a c-ben tallhat karakter vizsglatt! && magasabb precedencij ||-nl, de mindketten alacsonyabb precedencijak, mint a relcis s egyenlsgopertorok, gy az olyan kifejezsek, mint i < lim - 1 && (c = getchar()) != \n && c != EOF kln zrjeleket nem ignyelnek. De mivel a != precedencija magasabb, mint az rtkads, a kvnt eredmny elrse rdekben a (c = getchar()) != \n kifejezsben zrjelekre van szksg. A ! egyoperandus negl opertor a nemnulla, msszval igaz operandusbl 0-t, a nulla, azaz hamis operandusbl pedig 1-et csinl. A ! opertort ltalban olyan szerkezetekben hasznljk, mint pl. if (! inword), s ezzel helyettestjk az if (inword == 0) formt. Nehz ltalnossgban megmondani, hogy melyik alak a jobb. Az elbbi ltalban jl olvashat (ha nem sz belsejben vagyunk), bonyolultabb esetben azonban nehezen rthet. 2.1. Gyakorlat. Irjunk az elz, for ciklussal egyenrtk ciklust, amely a &&-et hasznlja! 2.7. Tpuskonverzik Ha egy kifejezsben klnbz tpus operandusok fordulnak el, a kifejezs kirtkelshez az operandusokat azonos tpusakk kell alaktani. ltalban csak az rtelmes konverzik trtnnek meg automatikusan, pldul egsz tpus mennyisgek talaktsa

68

lebegpontoss olyan kifejezsekben, mint f + i, ahol f float, i pedig int tpus. Az rtelmetlen kifejezsek, mint pldul a float indexknt val hasznlata, nem megengedettek. A char s int tpus mennyisgek aritmetikai kifejezsekben szabadon keveredhetnek: a kifejezsekben elfordul minden char automatikusan int-t alakul t. Ez nagymrv rugalmassgot tesz lehetv bizonyos karaktertranszformcikban. Plda erre az atoi fggvny, amely egy szmjegyekbl ll karakterlncot a megfelel numerikus rtkk alakt t: atoi (s) / * s egssz alaktsa*/ char s []; { int i, n; n = 0; for (i = 0; s [i] >= 0 && s [i] <= 9; ++i) n = 10 * n + s [i] - 0; return (n); } Amint az 1. fejezetben emltettk, az s [i] - 0 kifejezs ellltja az s[i] -ben trolt karakter numerikus rtkt, mivel a 0, 1 stb. rtkek folytonosan nvekv pozitv sorozatot alkotnak. A char-bl int-t trtn talakts msik pldja az albbi lower fggvny, amely egyetlen karaktert alakt t kisbetss, kizrlag ASCII karakterkszlet esetn. Ha a karakter nem nagybet, a lower vltozatlanul adja vissza. lower /*c konvertlsa kisbetss; csak ASCII*/ int c; { if (c >= A && c <= Z) return (c + a - A); else return ; } Ez a program csak az ASCII kdkszlet hasznlata esetn mkdik helyesen, mivel abban a megfelel kis- s nagybetk tvolsga rgztett, mind a kisbets, mind a nagybets bc numerikus rtkei folytonosan kvetik egymst - A s Z kztt csak betk vannak. Az EBCDIC karakterkszletre (IBM 360/370) ez az utbbi tulajdonsg nem rvnyes, gy lower nem mkdne helyesen - nem csak betket konvertlna. A karaktereknek egsz szmokk trtn talaktsval kapcsolatban megemltjk a nyelv egy finomsgt. A C nyelv nem hatrozza meg, hogy a char tpus vltozk eljeles vagy eljel nlkli mennyisgek-e. Krds teht, hogy egy char mennyisg int tpusv alaktsakor ltrejhet-e negatv egsz is? Sajnos ez az architektrtl fggen gprl gpre vltozik. Bizonyos gpeken (pldul a PDP-11-en) az olyan char, amelynek legbaloldalibb bitje 1,negatv egssz alakul t (eljel-kiterjeszts: sign extension).Ms gpeken a char oly mdon vlik int mennyisgg, hogy a szmtgp a sz bal oldalhoz nullkat illeszt, s gy a keletkez rtk mindig pozitv. A C nyelv defincija garantlja, hogy a gp szabvnyos karakterkszletben tallhat egyetlen karakter sem lesz negatv, gy ezeket a karaktereket szabadon hasznlhatjuk kifejezsekben pozitv mennyisgekknt. Ha azonban ms, tetszleges bit-mintkat trolunk

69

karakter tpus vltozkban, azok egyes gpeken pozitv szmknt, msokon negatv szmknt jelenhetnek meg. Tipikus pldja ennek, amikor EOF-nak a -1 rtket hasznljuk. Tekintsk a char c; c = getchar(); if (c == EOF) ... programrszt! Olyan gpen, amely nem vgez eljel-kiterjesztst, c mindig pozitv, mivel char-nak deklarltuk, EOF viszont negatv. gy a felttel sohasem teljesl. Ennek elkerlse rdekben gyeltnk arra, hogy minden olyan vltozt int-nek s ne char-nak deklarljunk, amely a getchar fggvny ltal visszaadott rtket tartalmaz. Valjban persze nem csak az esetleges eljel-kiterjeszts miatt hasznlunk int-et char helyett. Egyszeren arrl van sz, hogy a getchar fggvnynek minden lehetsges karaktert vissza kell adnia (oly mdon, hogy az brmilyen jabb programbemenethez felhasznlhat legyen), de vissza kell adnia az ezektl klnbz EOF rtket is! gy a getchar fggvny rtke nem jelenhet meg char-knt, hanem azt int-knt kell trolni. Az automatikus tpuskonvertl msik hasznos formja, hogy a relcis kifejezsek (pl. i > j) s az &&, ill. || szimblumokkal sszekapcsolt logikai kifejezsek gy az isdigit = c >= 0 && c <= 9; rtkads az isdigit vltoznak az 1 rtket adja, ha c szmjegy s a 0 rtket ha nem az. (Az if, while, for stb. felttelvizsglatban az igaz jelentse egyszeren: nemnulla.) Az implicit aritmetikai konverzik mkdse teljesen rtelemszer. ltalban, ha egy ktoperandus opertor, mint a vagy a * operandusai klnbz tpusak, a program a mvelet elvgzse eltt az alacsonyabb tpus vltozt magasabb tpusv alaktja t. Az eredmny a magasabb tpus. Pontosabban szlva az aritmetikai opertorok az albbi konverzis szablyok szerint hatnak: A char s short mennyisgek int tpusv, a float mennyisgek double tpusv alakulnak t. Ezutn ha az egyik operandus double, a msik is double tpusv alakul t, s az eredmny is double. Egybknt ha az egyik operandus long, a msik is long tpusv alakul t, s az eredmny is long lesz. Egybknt ha az egyik operandus unsigned, a msik is unsigned tpusv alakul t, s az eredmny is unsigned lesz. Egybknt az operandusoknak int tpusaknak kell lennik, s az eredmny int. Jegyezzk meg, hogy egy kifejezsben elfordul minden float mennyisg double-l alakul t: a C-ben minden lebegpontos mvelet ktszeres pontossg! Az rtkads is tpuskonverzival jr: a jobb oldal rtke talakul bal oldali tpusv, s ez lesz egyben az eredmny tpusa is. A karakterek egssz alakulnak t - akr rtke definciszeren 1 , ha a kifejezs igaz, ill. 0, ha hamis.

70

eljel-kiterjesztssel, akr anlkl -, amint azt az elbbiekben ismertettk. Az ellenttes irny mvelet, az int-bl char-ba trtn konverzi egyrtelm - a felesleges magas helyirtk bit-ek egyszeren elmaradnak. gy int i; char c; i = c; c = i; esetben c rtke nem vltozik. Ez mindig igaz, fggetlenl attl, hogy van-e eljel-kiterjeszts vagy sem. Ha x float s i int, akkor: x=i valamint i= x egyarnt konverzihoz vezet; a float-bl int-be trtn konverzi a trt rsz levgst eredmnyezi. A double kerektssel alakul t float-t. A hosszabb int-ek rvidebbekk vagy char-okk gy alakulnak t, hogy a program a felesleges magas helyirtk bite-ket levgja. Mivel a fggvnyargumentumok kifejezsek, a fggvnyeknek trtn argumentumtads ugyancsak tpuskonverzikkal jr. Konkrtan a char s a short int-t vlik, a float pedig double mennyisgg. Ezrt deklarltuk a fggvnyargumentumokat int-nek s double-nak mg akkor is, amikor a fggvnyt char-ral s float-tal hvtuk meg. Vgezetl tetszleges kifejezsben is kivlthatunk, kiknyszerithetnk tpuskonverzit, ha n. tpusmdost (cast) szerkezetet hasznlunk. A (tipusnv) kifejezs szerkezetben a kifejezs az elz szablyok alkalmazsval az elrt tpusv alakul t, gy mintha a kifejezs hozz lenne rendelve egy, a megadott tpus vltozhoz, amelyet azutn az egsz szerkezet helyett hasznlunk. Pldul az sqrt (gykvon) knyvtri rutin double tpus argumentumot vr, s rtelmetlen eredmnyt ad, ha vletlenl valami mst kap. Ha teht n egsz tpus, akkor sqrt ((double) n) az n-et a sqrt-nek trtn tads eltt double-l konvertlja. (Jegyezzk meg, hogy a tpusmdost szerkezet n rtkt a kvnt tpusban szolgltatja; n tnyleges tartalma azonban nem vltozik.) A tpusmdost opertor precedencija ugyanaz, mint a tbbi egyoperandus opertor, amint azt a fejezet vgn kzlt sszefoglal tblzat is mutatja. 2.2. Gyakorlat. Irjuk meg a htoi(s) fggvnyt, amely egy hexadecimlis szmjegyekbl ll karakterlncot a neki megfelel egsz rtkk alakt t! A megengedett szmjegyek; 0...9, a...f s A...F. 2.8. Inkrementl s dekrementl opertorok A C nyelv tartalmaz kt szokatlan opertort, amelyekkel vltozk inkrementlhatk s dekrementlhatk. A ++ inkrementl opertor operandushoz 1 -et ad hozz, adekrementl opertor pedig 1 -et von le belle. A ++-t gyakran hasznljuk vltozk inkrementlsra, pldul: if (c == \n) ++nl; A szokatlansg abban rejlik, hogy a ++ s aegyarnt hasznlhat prefix opertorknt (a vltoz eltt, mint a ++n esetben) vagy postfix opertorknt (a vltoz mg rva: n++).

71

Az eredmny mindkt esetben n inkrementlsa. De mg a ++n kifejezs n-et az eltt nveli, hogy felhasznln annak rtkt, n++ csak azt kveten inkrementl. Eszerint olyan esetekben amikor nemcsak az inkrementl tulajdonsgot, hanem n rtkt is felhasznljuk, ++n s n++ klnbznek egymstl. Ha n rtke 5, akkor x = n++; az x-et 5-re lltja, de x = ++n; x-et 6-ra lltja. n mindkt esetben 6 lesz. Az inkrementl s dekrementl opertorok csak vltozkra alkalmazhatk; az olyan kifejezs, mint x = (i + j)++ nem megengedett ! Ha az rtkre nincs szksg, csak az inkrementl hatsra, pl. if (c == \n) nl++; esetben, a prefix vagy a postfix opertort tetszs szerint vlaszthatjuk meg. Vannak azonban olyan feladatok, amikor specilisan az egyikre vagy a msikra van szksg. Tekintsk pldul a squeeze(s, c) fggvnyt, amely az sszes elfordul c karaktert trli az s karakterlncbl: squeeze (s, c) /*Valamennyi c karakter trlse s-bl*/ char s []; int c; { int i, j; for (i = j = 0; s [i] != \0; i++) if (s [i] != c) s [j++] = s [i]; s [j] = \0; } Minden alkalommal, amikor a program az s karakterlncban

c-vel nem azonos karaktert tall, bemsolja azt a pillanatnyi j pozciba, s csak ezutn inkrementlja j-t, hogy fogadhassa a kvetkez karaktert. Hatsa pontosan azonos az if (s [i] != c) { s [j] = s [i]; j++; } alakval. Hasonl plda fordult el az 1. fejezetben ltott getline fggvnyben, ahol az if (c == \n) { s [i] = c; ++i; } sorokat az ennl tmrebb if (c == \n) s [i++] = c; alakkal helyettesthetjk.

72

Harmadik pldnk az strcat(s, t) fggvny, amely a t karakterlncot az s karakterlnc vghez illeszti (konkatenlja). strcat felttelezi, hogy s-ben elg hely van ahhoz, hogy ott az sszeillesztett karakterlnc elfrjen. strcat (s,t) /*t illesztse s vghez*/ char s[], t []; / * s-nek elg nagynak kell lennie * / { int i, j; i = j = 0; while (s[i] != \0) /*Keresi s vgt*/ while (s [i] = \ ) / eresi s vg / i++; while ((s [i++] = t[j++]) != \0) /*t tmsolsa*/ ; } Mikzben a program az egyes karaktereket t-bl s-be msolja, a ++ postfix opertor mind i, mind pedig j rtkt nveli, hogy azok a kvetkez ciklusban a megfelel pozcira mutassanak. 2.3. Gyakorlat. rjuk meg az squeeze(s1 , s2) egy msik vltozatt, amely s1-bl minden olyan karaktert trl, amely megegyezik brmelyik s2 beli karakterrel! 2.4. Gyakorlat. rjuk meg az any(s1, s2) fggvnyt, amely megadja az s1 karakterlncnak azt a legels pozcijt, ahol brmelyik, s2 karakterlncbeli karakter elfordul, s -1 rtket szolgltat, ha s1 egyetlen s2-beli karaktert sem tartalmaz! 2.9. Bitenknti logikai opertorok A C nyelvben tbb bitmanipulcis opertor van; ezek a float s double tpus vltozkra nem alkalmazhatk. & bitenknti S, | bitenknti megenged (inkluzv) VAGY, ^ bitenknti kizr (exkluzv) VAGY, << bitlptets (shift) balra, >> bitlptets (shift) jobbra, ~ egyes komplemens (egyoperandus). A bitenknti S opertort gyakran hasznljuk valamely maszkolsra. Pldul:

bithalmaz

c = n & 0177; mindent nullz, az n kis helyirtk bitjeinek kivtelvel. A | bitenknti VAGY opertorral lehet biteket 1 -re lltani. x = x | MASK; ugyanazokat a biteket lltja 1-be x-ben, mint amelyek 1-be vannak lltva MASK-ban. Gondosan meg kell klnbztetnnk az & s | bitenknti opertorokat az && s || logikai mveletektl, amelyek egy igazsgrtk balrl jobbra trtn kirtkelst rjk el. Ha pldul x rtke 1 s y rtke 2, akkor x & y rtke 0, x&&y rtke pedig 1 . (Mirt?) A << s >> lptet (shift) opertorok bal oldali operandusukon annyi bitlptetst hajtanak vgre, ahny bitpozcit a jobb oldali operandusuk elr. Igy x <<2 az x-et kt pozcival balra lpteti, a megrlt biteket pedig 0-val tlti fel; ez 4-gyel val szorzssal egyenrtk. unsigned mennyisg jobbra lptetse esetn a felszabadul bitekre nullk kerlnek. Eljeles mennyisg jobbra lptetse esetn bizonyos gpeken, gy a PDP-11-en a felszabadul bitekre az eljel kerl (aritmetikai lptets), ms gpeken 0 bitek (logikai lptets).

73

A ~ binris opertor egsz tpus mennyisg 1 -es komplemenst kpezi, vagyis minden 1 -es bitet 0-ra llt s viszont. Ezt az opertort leggyakrabban olyan kifejezsekben hasznljuk, mint x & ~077 amely x utols 6 bitjt 0-ra maszkolja. Vegyk szre, hogy x & ~077 fggetlen a szhosszsgtl, s gy elnysebb, mint pldul x & 0177700, amely felttelezi, hogy x 16 bites mennyisg. A gpfggetlen alak nem nveli a futsi idt, mivel ~077 lland kifejezs, s mint ilyen, fordtsi idben rtkeldik ki. Kvetkez programpldnkban nhny bitopertor mkdst szemlltetjk. A getbits(x, p, n) fggvny x-nek a p-edik pozcin kezdd n-bites mezjt adja vissza (jobbra igaztva). Felttelezzk, hogy a 0 bitpozci a jobb szlen van s hogy n s p rtelmes pozitv rtkek. Pldul getbits (x,4,3) a 4, 3 s 2 pozcin lev hrom bitet szolgltatja, jobbra igaztva. getbits (x, p, n) p, n; /*n bit a p pozcitl kezdve*/ unsigned x,

{ return ((x >> (p + 1 - n)) & ~(~0 << n)); } x >> (p + 1 - n) a kvnt mezt a sz jobb szlre mozgatja. Az x argumentumot unsigned mennyisgnek deklarlva biztostjuk, hogy a jobbra lptetskor a felszabadul bitek ne eljelbitekkel, hanem nullkkal tltdjenek fel, fggetlenl attl, hogy a program ppen milyen gpen fut. ~0 csupa 1 bitet jelent, amelyet az ~0 << n utasts segtsgvel n bitpozcival balra lptetve a jobb oldali n biten csupa nullkbl ll, a tbbi pozcin egyesekbl ll maszk jn ltre. Ezt a ~ opertorral komplementlva olyan maszk keletkezik, amelyben a jobb oldali biteken llnak egyesek. 2.5. Gyakorlat. Mdostsuk a getbits fggvnyt gy, hogy a bitpozcik sorszma balrl jobbra njn! 2.6. Gyakorlat. Irjunk olyan wordlength() fggvnyt, amely kiszmtja a befogad gp szhosszsgt, azaz meghatrozza, hogy egy int mennyisgben hny bit van! A fggvny legyen gpfggetlen, vagyis a forrskd minden gpen mkdjn! 2.7. Gyakorlat. Irjunk olyan rightrot(n, b) fggvnyt, amely b szm bitpozcival jobbra trtn bitrotcit vgez az n egsz tpus mennyisgen! 2.8. Gyakorlat. Irjunk olyan invert(x, p, n) fggvnyt, amely az xben a p pozcitl kezdve n bitet invertl(vagyis az 1-eseket 0ra, a 0-kat 1-esekre cserli fel), mikzben a tbbi bit vltozatlan marad! 2.10. rtkad opertorok s kifejezsek Az olyan kifejezsek, mint i = i + 2 amelyekben a bal oldal a jobb oldalon megismtldik, a += rtkad opertor segtsgvel az i += 2 tmrtett alakban is rhatk. A C-ben a legtbb ktoperandus opertornak megvan az op= alak rtkad megfelelje, ahol op a + - * / % << >> & | szimblumok egyike. Ha e1 s e2 kifejezs, akkor

74

e1 op= e2 jelentse: e1 = (e1) op (e2). Az egyetlen eltrs, hogy az elbbi esetben a gp e1-et csak egyszer szmtja ki. gyeljnk az e2 krli zrjelekre: x *= y + 1 jelentse x = x * (y + 1) nem pedig x = x * y + 1 Az albbi pldban a bitcount fggvny megszmllja az egsz tpus argumentumban tallhat 1 -es bitek szmt. bitcount (n) /*1-es bitek megszmllsa n-ben*/ unsigned n; { int b; for (b = 0; n != 0; n >>= 1) if (n & 01) b++; return (b); } Tmrsgk mellett az rtkad opertoroknak elnye az is, jobban megfelelnek az emberi gondolkodsmdnak. Azt mondjuk: adj 2-t i-hez vagy nveld i-t 2-vel (teht: i += 2), nem pedig: vedd i-t, adj hozz 2-t majd tedd vissza az eredmnyt i-be (i = i + 2). Bonyolult kifejezsekben mint yyval [yypv [p3 + p4] + yypv [p1 + p2]] += 2 az rtkad_opertor rthetbb teszi a kdot, mivel az olvasnak nem kell krlmnyesen ellenriznie, hogy kt hossz kifejezs tnyleg megegyezik-e; ha pedig nem egyezik meg, nem kell azon tndnie, hogy mirt nem. Ezenkvl az rtkad opertor mg a fordtnak is segthet a hatkonyabb kd ellltsban. Korbban mr kihasznltuk azt a tnyt, hogy az rtkad utastsnak rtke van s kifejezsekben is elfordulhat; a legkznsgesebb plda: while ((c = getchar()) != EOF) . . . Ugyangy, a tbbi rtkad opertort hasznl rtkadsok szerepelhetnek kifejezsekben, br ezek ritkbban fordulnak el. Az rtkad kifejezs tpusa megegyezik bal oldali operandusnak tpusval. 2.9. Gyakorlat. 2-es komplemens aritmetikban x & (x-1 ) trli x legjobboldalibb 1-es bitjt. (Mirt?) Kihasznlva ezt a megfigyelst, rjuk meg a bitcount egy gyorsabb vltozatt! 2.11. Feltteles kifejezsek Az if (a > b) z = a; else z = b; feltteles utasts eredmnyeknt z a s b kzl a nagyobbik rtkt veszi fel. is hogy

75

A C-ben a hromoperandus ?: opertor segtsgvel az ilyen szerkezeteket sokkal rvidebben lerhatjuk. Legyen e1, e2, e3 hrom kifejezs. Az e1 ? e2 : e3 feltteles kifejezsben a gp elszr e1-et rtkeli ki. Ha rtke nem nulla (igaz), akkor e2, egybknt e3 kirtkelse kvetkezik, s a kapott rtk lesz a feltteles kifejezs rtke. A program e2 s e3 kzl teht csak az egyiket rtkeli ki. gy z-be a s b kzl a nagyobbat az albbi feltteles kifejezssel tlthetjk: z = (a > b) ? a : b; _/* z = max(a,b) */ Megjegyezzk, hogy a feltteles kifejezs is igazi kifejezs, s ugyangy hasznlhat, mint brmilyen ms kifejezs. Ha e2 s e3 klnbz tpus, az eredmny tpust a fejezetnkben korbban ismertetett konverzis szablyok hatrozzk meg. Ha pldul f float s n int, akkor az (n > 0) ? f : n kifejezs double lesz, fggetlenl attl, hogy n pozitv-e vagy sem. A feltteles kifejezsben az els kifejezst nem ktelez zrjelbe tenni, mivel ?: precedencija igen alacsony (pontosan az rtkads fltti). Zrjelezssel azonban rthetbb tehetjk a kifejezs felttelrszt. A feltteles kifejezsek hasznlata gyakran tmr s vilgos kdot eredmnyez. Az albbi ciklus pldul soronknt tzesvel kinyomtatja egy tmb N elemt oly mdon, hogy az egyes oszlopokat egy-egy szkz vlasztja el, s minden sort (az utolst is belertve) pontosan egy jsor karakter zr le. for (i = 0; i < N; i++) printf (%6d %c, a[i], (i % 10_== 9 || i == n - 1)_? \n : ); Minden tizedik s az N-edik elem utn egy jsor karaktert ad ki a program. Minden ms elemet egy-egy szkz kvet. Gyakorlskppen prblja meg az olvas ugyanezt feltteles kifejezs hasznlata nlkl lerni! 2.10. Gyakorlat. rjuk t a lower fggvnyt, amely a nagybets karaktereket kisbetsekk konvertlja! Az if-else helyett hasznljunk feltteles kifejezst! 2.12. Precedencia; a kirtkels sorrendje A kvetkez tblzat sszefoglalja valamennyi opertor precedencia- s ktsi szablyait, azokt is, amelyekrl idig nem volt sz. Az egy sorba rt opertorok precedencija azonos; a tblzatban lefel haladva a precedencia cskken, gy pldul * , / s % precedencija azonos s magasabb + s precedencijnl. Opertor Asszociativits () [] balrl jobbra ! ~ ++ -- - (tipus) * & . -> sizeof jobbrl balra * / % balrl jobbra + balrl jobbra << >> balrl jobbra < <= > >= balrl jobbra == != balrl jobbra

76

& ^ | && || ? : = += -= stb. , (3. fejezet)

balrl jobbra balrl jobbra balrl jobbra balrl jobbra balrl jobbra jobbrl balra jobbrl balra balrl jobbra

A -> s . opertorok segtsgvel struktrk elemeihez frhetnk hozz, ezekkel, valamint a sizeof (objektum mrete) opertorral a 6. fejezetben foglalkozunk. A * (indirekci) s az & (valaminek a cme) opertorral az 5. fejezetben tallkozunk. gyeljnk arra, hogy az &, ^ s | bitenknti logikai opertorok precedencija kisebb, mint az == s != precedencija. Emiatt az olyan bitvizsgl kifejezsek, mint if ((x & MASK) == 0) . . . a zrjelezs nlkl nem mkdnek helyesen. Mint emltettk, az asszociatv s kommutatv opertorokkal (*, +, &, ^, |) felptett kifejezseket a fordtprogram trendezheti, mg akkor is, ha zrjele(ket)t tartalmaznak. Az esetek tbbsgben ennek nincs jelentsge; azokban az esetekben, ahol mgis van, explicit ideiglenes vltozk hasznlatval gondoskodhatunk a kvnt kirtkelsi sorrendrl. A legtbb nyelvhez hasonlan a C sem hatrozza meg egy-egy opertor operandusainak kirtkelsi sorrendjt. Az x = f () + g (); utastsban pl. nem tudjuk, hogy f-et g eltt vagy g utn szmtja ki a gp. gy, ha akr f, akr g olyan kls vltozt mdost, amelytl a msik fgg, x rtke fgghet a mveletek vgrehajtsnak sorrendjtl. Ha adott sorrendre van szksgnk, ezt megint csak gy biztosthatjuk, hogy a rszeredmnyeket ideiglenes vltozkban troljuk. Ugyancsak hatrozatlan a fggvnyargumentumok kirtkelsi sorrendje, gy a printf (%d %d \n, ++n, power (2,n)); /* ROSSZ */ utasts klnbz gpeken klnbz eredmnyeket adhat (s ad is) attl fggen, hogy a gp n-et a power hvsa eltt vagy utn inkrementlja. A helyes megolds termszetesen: ++n; printf (%d %d \n, n, power(2,n)); A fggvnyhvsok, egymsba skatulyzott rtkad utastsok, az inkrementl s dekrementl opertorok mellkhatsokat okozhatnak. Ez azt jelenti, hogy egy kifejezs kiszmtsnak nem szndkos - mellktermkeknt megvltozhat egy vltoz rtke. A mellkhatsokkal jr kifejezsekben sok fgghet attl, milyen sorrendben trolja a gp a kifejezsben szerepl vltozkat. Szerencstlen, de elg gyakori esetet pldz az a [i] = i++; utasts. Krds, hogy az index i rgi vagy j rtkvel azonos. A vlasz klnbz lehet, aszerint, hogy a fordt

77

hogyan rtelmezi, kezeli ezt az utastst. Mindig a fordt dnti el teht, lesz-e mellkhats (mdosul-e a vltozk rtke) vagy sem, hiszen az optimlis sorrend ersen fgg a gp architektrjtl. A tanulsg: egy nyelven sem szabad olyan programot rni, amelynek eredmnye fgg a konkrt kirtkelsi sorrendtl! Termszetesen j, ha tudjuk, mire vigyzzunk, ugyanakkor, ha nem tudjuk, hogy valami hogyan mkdik klnbz gpeken, ez a tudatlansg meg is vdhet bennnket. (A lint nev C helyessgvizsgl program a legtbb esetben felfedezi a kirtkelsi sorrendtl val fggst.) _ 3. fejezet: Vezrlsi szerkezetek

A nyelv vezrlstad utastsai a szmtsok vgrehajtsnak sorrendjt hatrozzk meg. A korbbi pldkban mr tallkoztunk a C leggyakoribb vezrlstad utastsaival. Ebben a fejezetben teljess tesszk a kszletet s rszletesen ismertetjk a mr korbban emltett utastsokat is. 3.1. Utastsok s blokkok A kifejezsek, pl. x = 0, i++ vagy vlnak, ha pontosvessz kveti ket: x = 0; i++; printf (. . .); A C-ben a pontosvessz utastslezr jel (termintor) s nem elvlaszt szimblum, mint az ALGOL-szer nyelvekben. A { s } kapcsos zrjelek felhasznlsval deklarcikat s utastsokat egyetlen sszetett utastsba vagy blokkba foghatunk ssze. Ez szintaktikailag egyetlen utastssal egyenrtk. Nyilvnval pldi ennek a fggvnyek utastsait hatrol kapcsos zrjelek, vagy azok a zrjelek, amelyek egy if, else, while vagy for szimblumot kvet utastssort vesznek krl. (Vltozk brmely blokkon bell deklarlhatk, errl a 4. fejezetben lesz sz.) A blokkot lezr jobb oldali kapcsos zrjel utn soha nincs pontosvessz. 3.2. Az if-else utasts Az if-else utastssal dntst, vlasztst runk le. Az utasts szintaxisa formlisan : if (kifejezs) 1.utasts else 2.utasts ahol az else rsz nem ktelez. A gp a kifejezs kirtkelse utn, ha annak rtke igaz (vagyis nemnulla), az 1. utastst, ha rtke hamis (nulla), s ha van else rsz, akkor a 2. utastst hajtja vgre. Mivel az if egyszeren a kifejezs numerikus rtkt vizsglja, lehetsg van bizonyos programozsi rvidtsre. A legnyilvnvalbb, ha if (kifejezs) t runk if (kifejezs != 0) helyett. Ez nha termszetes s vilgos, mskor viszont nehezen megfejthet. Minthogy az if-else konstrukci else rsze elhagyhat, sokszor nem egyrtelm, hogy az egymsba skatulyzott if utastsok printf(. . .) utastss

78

melyikhez tartozik else g. A ktrtelmsget a C ms nyelvekhez hasonlan azzal oldja fel, hogy az else a hozz legkzelebbi else nlkli if-hez kapcsoldik. Pldul az if (n > 0) if (a > b) z = a; else z = b; esetben az else a bels if sorbetolssal szemlltettk. Ha nem rhetjk el a helyes sszerendelst: if (n > 0) { if (a > b) z = a; } else z = b; A ktrtelmsg klnsen veszlyes az olyan esetekben, mint: if (n > 0) for (i = 0; i < n; i++) if (s[i] > 0 ) { printf (. . .); return (i); } else /*ROSSZ*/ printf(hiba, n rtke nulla \n); A sorbetols ugyan flrerthetetlenl mutatja, hogy mit akarunk, de ezt a szmtgp nem rzkeli, s az else-t a bels if-hez kapcsolja. Az ilyen tpus hibkat igen nehz felfedezni. Egybknt vegyk szre, hogy a z = a utn pontosvessz van az if (a > b ; z = a; hez tartozik, ezt akarjuk, amint azt a zrjelekkel

else z = b; programrszben. Ennek az az oka, hogy nyelvtanilag egy utasts kveti az if-et, mrpedig az olyan kifejezs jelleg utastsokat is, mint z = a mindig pontosvessz zrja le. 3.3. Az else-if utasts Az if (kifejezs) utasts else if (kifejezs) utasts else if (kifejezs) utasts else utasts szerkezet kln fejtegetst. olyan gyakran fordul el, hogy megr nmi

Tbbszrs elgazst (dntst) ltalban ilyen if-sorozattal valstunk meg. A gp sorban kirtkeli a kifejezseket. Ha valamelyik kifejezs igaz, akkor a hozz tartoz utastst a gp vgrehajtja, s ezzel az egsz lnc lezrul. Az egyes utastsok helyn egyetlen utasts vagy kapcsos zrjelek kz zrt utastscsoport egyarnt llhat.

79

Az utols else a fentiek kzl egyik sem (alaprtelmezs szerinti) esetet kezeli. Ha a vezrls ide kerl, egyetlen korbbi felttel sem teljeslt. Nha ilyenkor semmit sem kell csinlni, gy a zr else utasts elhagyhat, vagy - valamilyen hibaellenrzsre hasznlhat.

tiltott

felttel

figyelsvel

Kvetkez pldnkban egy hromutas dntst lthat az olvas. Olyan binris keres fggvnyt mutatunk be, amely egy rendezett v tmbben egy bizonyos x rtket keres. v elemeinek nvekv sorrendben kell kvetnik egymst. Ha x elfordul v-ben, akkor a fggvny x v-beli (0 s n-1 kztti) sorszmt szolgltatja, ellenkez esetben rtke -1 lesz: binary (x, v, n) /*x keresse v[0] . . . v[n - 1]-ben*/ int x, v[], n; { int low, high, mid; low = 0; high = n - 1; while (low <= high) { mid = (low + high) / 2; if (x < v[mid]) high = mid - 1; else if (x > v[mid]) low = mid + 1; else / *Megtallta*/ return (mid); } return (-1); } Minden lpsben meg kell vizsglni, hogy x kisebb, mint a v[mid] kzps elem, nagyobb nla vagy egyenl vele, ami egszen termszetes mdon rhat le else-if szerkezettel. 3.4. A switch utasts A switch utasts a tbbirny programelgaztats egyik eszkze. Megvizsglja, hogy valamely kifejezs rtke megegyezik-e tbb lland rtk valamelyikvel, s ennek megfelel ugrst hajt vgre. Az 1. fejezetben olyan programot lttunk, amellyel az egyes szmjegyek, res s egyb karakterek elfordulsait szmlltuk meg. Ugyanazt a programot most az if ... else if... ...else szerkezet helyett a switch utastssal rtuk meg: main () /*Szmjegyek, szmllsa*/ res s egyb karakterek

{ int c, i, nwhite, nother, ndigit[10]; nwhite = nother = 0; for (i = 0; i < 10; i++) ndigit [i] = 0; while ((c = getchar()) != EOF) switch { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: ndigit [c - 0] ++; break;

80

case : case \n: case \t: nwhite++; break; default : nother+ +; break; } printf (szmjegyek=); for (i = 0; i < 10; i++) printf (%d, ndigit[i]); printf (\n res hely = %d, egyb = %d \n, nwhite, nother); } A switch kirtkeli a zrjelek kztti kifejezst (ebben a programban ez a c karakter), s sszehasonltja az sszes case (eset) rtkvel. Minden case-t egsz rtkkel, karakterllandval vagy lland kifejezssel meg kell cimkzni. Ha valamelyik case azonos a kifejezs rtkvel, a vgrehajts ennl a case-nl kezddik. A default cimkj case-re akkor kerl a vezrls, ha a tbbi case egyike sem teljesl. A default elhagyhat : ha nem szerepel s a case-ek egyike sem teljesl, semmi nem trtnik. A case-ek s a default tetszleges sorrendben kvethetik egymst. A case utastsok cmkinek klnbznik kell egymstl. A break utasts hatsra a vezrls azonnal kilp a switch-bl. Mivel a case-ek cmkeknt mkdnek, miutn valamelyik case-hez tartoz programrsz vgrehajtsa befejezdtt, a vezrls a kvetkez case-re kerl, hacsak explicit mdon nem intzkednk a kilpsrl. A switch-bl val kilps legkznsgesebb mdja a break s a return. Ugyancsak break utastssal lehet kilpni a while, for s do ciklusokbl, errl e fejezet ksbbi rszben lesz sz. Az egymst kvet case-ekbe val belps nem egyrtelmen elnys. A dolog pozitv oldala, hogy mint pldnkban a szkznl, az jsor s a tab karakternl is lttuk, egyetlen tevkenysg szmra tbb esetet enged meg. De ebbl az is kvetkezik, hogy ltalban minden case-t break-nek kell lezrnia, nehogy a vezrls a kvetkez case-re lpjen. A case-ken trtn lpkeds azrt is veszlyes, mert a vezrls szteshet, ha a programot mdostjuk. Azokat az eseteket kivve, amikor ugyanahhoz a szmtshoz tbb cmke tartozik, a caseek kztti tmenetek hasznlatval clszer takarkoskodni. A j klalak rdekben mg akkor is helyezznk el break-et az utols eset utn (az elz pldnkban a default utn), ha az logikailag szksgtelennek ltszik. Ha valamikor ksbb a szekvencia vghez jabb case-t illesztnk, ez a fajta defenzv programozsi taktika fog megmenteni minket. 3.1. Gyakorlat. Irjuk meg azt az expand(s, t) fggvnyt, amely - mikzben az s karakterlncot a t karakterlncba msolja - a lthatatlan karaktereket (pl.jsor s a tab) lthat escape szekvencikk (pl.\n s\t) alaktja t! Hasznljunk switch utastst! 3.5. A while s a for utasts Mr tallkoztunk a while s for ciklusokkal. A while (kifejezs) utasts szerkezetben a gp kirtkeli a kifejezst. Ha rtke nem nulla, akkor vgrehajtja az utastst s ismt kirtkeli a kifejezst. Ez a ciklus mindaddig folytatdik, amg a kifejezs 0 nem lesz, amikor is az utasts utn a vgrehajts vgetr.

81

A for (kif1; kif2; kif3) utasts alak for utasts egyenrtk a kif1; while (kif2) { utasts kif3; } alakkal. Nyelvtanilag a for mindhrom sszetevje kifejezs. Tbbnyire kif1 s kif3 rtkads vagy fggvnyhvs, kif2 pedig relcis kifejezs. A hrom kifejezs brmelyike elhagyhat, de a pontosvesszknek meg kell maradniuk. Ha kif1 vagy kif3 marad el, akkor a for( ;;) { . . . } vgtelen ciklus, amelybl valsznleg ms mdon kell kiugrani (pl. return vagy break rvn). A while s a for kztt lnyegben zlsnk szerint vlaszthatunk. Pldul a while ((c = getchar()) == || c == \n || c == \t) ; /*tugorja a lthatatlan karaktereket*/ programrszben nincs inicializls, sem jrainicializls, while hasznlata a lehet legtermszetesebbnek tnik. A for nyilvnvalan elnysebb olyankor, amikor egyszer inicializls s jrainicializls fordul el, mivel a ciklust vezrl utastsok egyms kzelben, a ciklus tetejn jelennek meg. Ez a legszembetnbb a for (i = 0; i < N; i++) esetben, amely egy tmb els N eleme feldolgozsnak C nyelv megfogalmazsa, a FORTRAN s PL/1 DO ciklusnak megfelelje. Az analgia azonban nem teljes, mivel a for hatrai a cikluson bellrl vltoztathatk, s az i vezrlvltoz megtartja rtkt, amikor valamilyen oknl fogva a ciklus vgetr. Minthogy a for sszetevi tetszleges kifejezsek, a for ciklus nem korltozdik aritmetikai lptetsekre. Stilris szempontbl mgis helyesebb, ha a for-ban nem helyeznk el tle fggetlen szmtsokat; a for-t inkbb ciklusvezrl mveletekre tartsuk fenn. Nagyobb pldaknt bemutatjuk az atoi fggvny msik vltozatt. Az atoi fggvny egy karakterlncot a neki megfelel numerikus rtkk alakt t. Az itt kvetkez vltozat a korbbinl ltalnosabb: kezeli az esetleges vezet szkzket s az esetleges vagy + eljelet. (A 4. fejezet tartalmazza az atof fggvnyt, amely ugyanezt a konverzit lebegpontos szmokra vgzi el. ) A program alapstruktrja a bemenet alakjt tkrzi: ugord t az res kzket, ha vannak olvasd be az eljelet, ha van ; egyszeren elmarad a kifejtsbl. Ha a kif2 vizsglat nem szerepel, akkor llandan igaznak tekintjk, gy

gy

82

olvasd be az egsz rszt s konvertld Minden lps elvgzi a maga feladatt, s a dolgokat tiszta llapotban adja t a kvetkez lpsnek. Az egsz folyamat az els olyan karakter elfordulsakor r vget, amely nem lehet rsze szmnak. atoi (s) /*s konvertlsa egssz*/ char s []; { int i, n, sign; for (i = 0; s [i] == || s [i] == \n || s [i] == \t; i++) ; /*Ugord t az res helyet*/ sign = 1; if (s [i] == + || s [i] == -) /*Eljelvizsglat*/ sign = (s [i++] == +) ? 1 : -1; for (n = 0; s [i] >= 0 && s [i] <= 9; i++) n = 10 * n + s [i] - 0; return (sign * n); } A ciklusvezrls tmritsnek elnyei mg jobban kitkznek, ha tbb, egymsba skatulyzott hurok van. A kvetkez fggvny az UNIX Shell sort funkcijt valsitja meg:feladata egy egsz tipus tmb rendezse. A Shell sort alapgondolata, hogy kezdetben inkbb az egymstol tvoli elemek kerljenek sszehasonltsra, nem pedig szomszdosak, mint az egyszer cserlgets rendezprogramokban. Ezltal a nagyfok kezdeti rendezetlensg vrhatan gyorsan eltnik, gy a ksbbi lpseknek kevesebb dolga akad. Az sszehasonltott elemek kztti tvolsg fokozatosan 1-re cskken, amikor is a rendezs szomszdcserlgetsi mdszerr alakul t. shell (v,n) n; int for for for { gap, (gap (i = (j = /*v[0]...v[n-1]-et nvekv sorba rendezi*/ int v[],

i , j, temp; = n / 2; gap > 0; gap /= 2) gap; i < n; i++) i - gap; j >= 0

&& v [j] > v[j + gap]; j -= gap) { temp = v [j]; v [j] = v [j + gap]; v [j + gap] = temp; } } Hrom egymsba skatulyzott ciklus van. A legkls ciklus az sszehasonltott elemek kztti tvolsgot vezrli, amit n/2-rl minden ciklusban felre cskkent, amg a tvolsg 0 nem lesz. A kzps ciklus minden olyan elemprt sszehasonlt, amelyek egymstl gap-nyire vannak. A legbels ciklus minden, nem megfelel sorrendben lev sszehasonltott elemprt megfordt. Mivel gap az utols ciklusban 1-re cskken, vgl minden elem helyes sorrendbe rendezdik. Vegyk szre, hogy a for utasts ltalnos jellegnl fogva a kls ciklus ugyanolyan alak, mint a tbbi, br nem vgez aritmetikai lptetst. Az egyik utols C opertor a , (vessz), amelyet legtbbszr a for utastsban hasznlunk. A vesszvel elvlasztott kifelyezsprok kirtkelse balrl jobbra trtnik, s az eredmny tpusa, ill. rtke megegyezik a jobb oldali operandus tpusval, ill. rtkvel. gy a for utasts egyes rszeiben tbb kifejezst is elhelyezhetnk pldul azrt, hogy prhuzamosan kt indexet dolgozzunk fel. Ezt

83

mutatjuk be a reverse(s) helyben megfordtja: reverse (s) [];

fggvnyben,

amely

az

karakterlncot

/*Az s karakterlnc helyben megfordtsa*/ char s

{ int c, i, j; for (i = 0 , j = strlen (s) - 1; i < j; i++ , j--) { c = s [i]; s [i] = s [j]; s [j] = c; } } A fggvnyargumentumokat, a deklarcikban elfordul vltozkat stb. elvlaszt vesszk nem vesszopertorok, s nem garantljk a balrl jobbra trtn kirtkelst. 3.2. Gyakorlat. rjuk meg az expand(s1, s2) fggvnyt, amely az s1 karakterlncban tallhat rvidtseket s2-ben teljes listv bvti ki (pl. a-z helyett abc. . .xyz-t r)! Engedjk meg a kiss a nagybetket, ill. a szmjegyeket is, s kszljnk fel az olyan esetek kezelsre is, mint a-b-c s a-z0-9 s -a-z! (Hasznos megllapods, ha a vezet vagy zr - karaktert bet szerint vesszk.) 3.6. A do-while utasts Mint az 1. fejezetben mondottuk, mind a while, mind a for ciklus rendelkezik azzal a kvnatos tulajdonsggal, hogy a kiugrsi felttel teljeslst nem a ciklus vgn, hanem a ciklus elejn vizsglja. A harmadik C-beli ciklusfajta, a do-while a vizsglatot a ciklus vgn, a ciklustrzs vgrehajtsa utn vgzi el; a trzs teht legalbb egyszer mindenkppen vgrehajtdik. A szintaxis: do utasts while (kifejezs); A gp elbb vgrehajtja az utastst, majd kirtkeli a kifelyezst. Ha az rtke igaz, ismt vgrehajtja az utastst, s gy tovbb. Ha a kifelyezs rtke hamiss vlik, a ciklus vgetr. Mint vrhat, a do-while-t sokkal ritkbban szoks hasznlni, mint a while-t s a for-t, taln az sszes ciklusok t szzalkban. Idnknt azonban mgiscsak rdemes elvenni, mint pldul az itt kvetkez itoa fggvnyben, amely egy szmot karakterlncc alakt t (atoi inverze).A feladat kicsit bonyolultabb, mint gondolnnk, mivel az egyszer szmjegygenerl mdszerek a szmjegyeket rossz sorrendben hozzk ltre.gy dntttnk, hogy a karakterlncot visszafel generljuk, majd megfordtjuk. itoa (n,s) n; { int i, sign; if ((sign = n) < 0) /*eljelvizsglat s trols*/ n = -n; /*n pozitv legyen*/ i = 0; do { /*szmjegyek generlsa fordtott sorrendben */ s [i++] = n % 10 + 0; szmjegyet*/ } while ((n /= 10) > 0); /*trli*/ /*megkapja a kvetkez /*n karakterr konvertlsa s-be*/ char s []; int

84

if (sign < 0) s [i++] = -; s [i] = \0; reverse (s); } Pldnkban a do-while hasznlata tnyleg knyelmes, mivel n rtktl fggetlenl legalbb egy karaktert el kell helyezni az s tmbben. A do-while trzst alkot egyetlen utastst - br itt szksgtelen kapcsos zrjelek kz zrtuk, hogy a siets olvas se higgye azt, hogy a while egy while ciklus kezdete. 3.3. Gyakorlat. Kettes komplemens szmbrzolsban az itoa fggmny ltalunk rt vltozata nem kezeli a legnagyobb negatv szmot, teht a (2 szmret-1) rtk n-et. Magyarzzuk meg, hogy mirt! Mdostsuk gy a programot, hogy ezt az rtket is helyesen rja ki, fggetlenl attl, hogy milyen gpen fut! 3.4. Gyakorlat. rjuk meg azt a hasonl itob (n,s) fggvnyt, amely az n unsigned egsz szmot binris karakterbrzolsban az s karakterlncba konvertlja! rjuk meg az itoh fggvnyt is, amely egy egsz szmot hexadecimlis brzolsmdba alakt t! 3.5. Gyakorlat. rjuk meg az itoa fggvnynek azt a vltozatt, amely kett helyett hrom argumentumot fogad! A harmadik argumentum a minimlis mezszlessg; az tkonvertlt szmot szksg esetn balrl res kzkkel kell kitlteni, hogy elg szles legyen. 3.7. A break utasts Nha knyelmes, ha a ciklusbl val kilpst nem a ciklus elejn vagy vgn val felttelvizsglattal vezreljk. A break utastssal a vizsglat eltt is ki lehet ugrani a for, while s do ciklusokbl, csakgy, mint a switch-bl. A break utasts hatsra a vezrls azonnal kilp a legbels zrt ciklusbl __t;_st_s h;_tsra a vezrlcs azonnal kilp a leghels zrt ciklusbl (vagy switch-bl). A kvetkez program az sszes sor vgrl eltvoltja a szkzket s tab karaktereket oly mdon, hogy break utasts segtsgvel kilp a ciklusbl, amikor a legjobboldalibb nem - szkz s nem tab karaktert megtallja. #define MAXLINE 1000 main () /*Sorvgi szkzk s tabok eltvoltsa*/ { int n; char line [MAXLINE]; while ((n = getline (line,MAXLINE)) > 0) { while (--n >= 0) if (line [n] != && line [n] != \t && line [n] != \n) break; line [n + 1] = \0; printf_(%s \n, line); } } A getline a sor hosszt adja vissza. A bels while ciklus a line utols karaktern kezddik (ne felejtsk el, hogyn elbb dekrementlja n-et s csak azutn hasznlja az rtkt), s visszafel haladva keresi az els olyan karaktert, amely nem szkz, tab vagy jsor. Ha ilyen karaktert tall, vagy ha n negatvv vlik (vagyis, ha az egsz sort megvizsglta), akkor a ciklus megszakad.

85

Igazolja az olvas, hogy ez akkor is helyes mkds, ha az egsz sor csupa res helyeket megjelent karakterekbl ll! A break alkalmazsa helyett vlaszthatjuk azt a megoldst is, hogy a vizsglatot magba a ciklusba tesszk: while ((n = getline (line,MAXLINE)) > 0) { while (--n >= 0 && (line [n] == || line [n] == \t || line [n] == \n)) ; . . . } Ez a vltozat gyengbb, mint az elz, mivel a vizsglat nehezebben rthet. ltalban kerljk az olyan vizsglatokat, amelyekben keverednek az &&, ||, ! szimblumok s a zrjelek. 3.8. A continue utasts A continue utasts a break-hez kapcsoldik, de a break-nl ritkbban hasznljuk; a continue-t tartalmaz ciklus (for, while, do) kvetkez itercijnak megkezdst idzi el. A while s a do esetben ez azt jelenti, hogy azonnal vgrehajtdik a felttelvizsglat, a for esetben pedig a vezrls azonnal az jrainicializlsi lpsre kerl. (A continue csak ciklusokra alkalmazhat, switch-re nem. Az olyan, switch-en belli continue, ahol a switch egy cikluson bell van, a kvetkez ciklusiterci vgrehajtst vltja ki.) Pldul a kvetkez programrsz az a tmbnek csak elemeit dolgozza fel; a negatv rtkeket tugorja: for (i = 0; i < n; i++) { if (a [i] < 0) /*Ugord t a negatv elemeket*/ continue; . . . /*Dolgozd fel a pozitv elemeket*/ } A continue utastst gyakran hasznljuk olyan esetekben, amikor a ciklus tovbbi rsze nagyon bonyolult s ezrt a felttelvizsglat megfordtsa s egy jabb programszint (sorbetols) tl mlyen skatulyzn a programot. 3.6. Gyakorlat. rjunk olyan programot, amely a bemenett a kimenetre msolja, de ha a bemenetre egyms utn tbbszr rkezik ugyanaz a sor, azt csak egyszer nyomtatja ki! (Ez egyszer vltozata az UNIX uniq szolgltatsnak.) 3.9. A goto utasts; cmkk A C-ben is hasznlhatjuk a sokat szidott goto utastst, ugrathatunk cmkkre. Elmletileg a goto-ra sohasincs szksg, s gyakorlatilag majdnem mindig egyszeren programozhatunk nlkle is. Ebben a knyvben nem hasznltunk goto-t. Mindazonltal bemutatunk nhny olyan esetet, ahol a goto-knak meg lehet a maguk helye. A leggyakoribb eset, amikor a feldolgozst valamilyen mlyen skatulyzott szerkezet belsejben akarjuk abbahagyni oly mdon, hogy egyszerre kt, egymsba gyazott ciklusbl lpnk ki. A break utastst kzvetlenl nem hasznlhatjuk, mivel az egyszerre csak a legbels ciklusbl ugratja ki a vezrlst. gy pldul : for ( . . . ) for ( . . . ) { a pozitv

86

. . . if (zavar) goto hiba; . . . } hiba: szmold fel a zavart Ez a fajta szervezs clszer, ha a hibakezel program nemtrivilis s ha a hibk klnbz helyeken fordulhatnak el. A cmkk alakja ugyanaz, mint a vltoznevek, csak kettspont kveti ket. Ugyanazon a fggvnyen bell, mint ahol a goto elfordul, brmelyik utastst megcmkzhetjk. Msik pldaknt tekintsk azt a problmt, amikor egy ktdimenzis tmb els negatv elemt akarjuk megtallni. (A tbbdimenzis tmbkrl az 5. fejezetben lesz sz.) Az egyik lehetsg: for (i = 0; i < n; i++) for (j = 0; j < m; j ++) if (v [i][j] < 0) goto found; /*Nem tallt*/ . . . found: /*Az i, j pozcin megtallta*/ . . . Brmely goto-t tartalmaz program megrhat goto nlkl, de esetleg csak megismtelt vizsglatok vagy kln bevezetett vltoz rn. Pldul a tmbben val keress goto nlkl : found = 0; for (i = 0; i < N && !found; i++) for (j = 0;j < M && ! found; j++) found = v[i][j] < 0; if (found) /*i-1, j-1-nl volt*/ . . . else /*Nem tallt*/ . . . Br nem kvnunk az gyben dogmatikusak lenni, kimondjuk : minl kevesebbet hasznljuk a goto-t, annl jobb. _ 4. fejezet: Fggvnyek s programstruktra A fggvnyek a nagy szmtsi feladatokat kisebbekre bontjk. gy a programoz pthet arra, amit msok mr megcsinltak, s nem kell mindent ellrl kezdenie. A jl megrt fggvnyek gyakran elrejtik a mveletek rszleteit a program azon rszei ell, amelyeknek nem is kell tudniuk rluk. Ezltal az egsz program vilgosabb vlik, s a vltoztatsok is knnyebben elvgezhetk. A C nyelvet gy terveztk meg, hogy a fggvnyek hatkonyak s knnyen hasznlhatk legyenek. A C programok ltalban sok kis mret fggvnyt tartalmaznak. Egy program tbb forrsllomnyra is tagoldhat. Az llomnyok kln-kln is fordthatk, s a knyvtrakban tallhat, mr korbban lefordtott fggvnyekkel

87

egytt betlthetk. Ezt a folyamatot most nem trgyaljuk, mivel a rszletek a helyi opercis rendszertl fggenek. A legtbb programoz mr ismeri a be- s kivitel cljra szolgl knyvtri fggvnyeket (getchar, putchar) s a numerikus szmtsok knyvtri fggvnyeit (sin, cos, sqrt). Ebben a fejezetben rszletesebben szlunk arrl, hogyan rhatunk j fggvnyeket. 4.1. Alapfogalmak Kezdetknt tervezznk s rjunk olyan programot, amely a bemenetnek minden olyan sort kinyomtatja, amely adott karakterlncbl ll mintt tartalmaz! (Ez specilis esete az UNIX grep segdprogramjnak.) Pldul a the minta keresse a Now is the time /Ideje, hogy for all good minden j men to come to the aid ember segtsgre siessen of their party. embertrsainak./ sorokban a Now is the time men to come to the aid of their party. kimeneti szveget fogja eredmnyezni. A knnyen felbonthat hrom klnll rszre: nyomtats Br nyilvn elhelyezhetjk az egsz programkdot a f rutinban, mgis az a jobb megolds, hogy kihasznljuk az elz termszetes struktrt s minden rszbl egy-egy kln fggvnyt ksztnk. Hrom kis program knnyebben kezelhet, mint egy nagy, mivel az egymsra nem tartoz rszletek a fggvnyekbe rejthetk s a nem kvnatos klcsnhatsok lehetsge minimlis lesz. Mi tbb, az egyes rszek a ksbbiekben nmagukban is hasznosak lehetnek. A while (van mg sor) feladatot az 1. fejezetben rt getline fggvny, a nyomtats feladatt pedig a szabvnyos knyvtrban rendelkezsnkre ll printf fggvny vgzi el. Eszerint csupn azt a rutint kell megrnunk, amely eldnti, hogy a sor tartalmazza-e a krdses mintt. A problma megoldsnak tervt a PL/1-bl lophatjuk el: az index(s, t) fggvny azt az s karakterlncbeli pozcit vagy indexet adja vissza, ahol a t karakterlnc kezddik, vagy pedig -1gyel tr vissza, ha s nem tartalmazza t-t. s-beli kezdpozciknt 0t hasznlunk, nem 1-et, mivel a tmbk a C nyelvben a 0 indexszel kezddnek. Ha a ksbbiekben bonyolultabb minta-sszehasonltsi feladatot akarunk megoldani, csak az index fggvnyt kell kicserlnnk; a programkd tbbi rsze vltozatlan marad. Ennyi tervezs utn mr gyorsan megrhatjuk a programot. Jl lthat, hogyan illeszkednek egymshoz az egyes rszek. Ne dolgozzunk a legltalnosabb esettel: a keresett minta egyelre legyen csupa betbl ll karakterlnc. Nemsokra sz lesz a karaktertmbk inicializlsrl, s az 5. fejezetben megmutatjuk, hogyan tehetjk a mintt olyan paramterr, amelyet a program futsa sorn lltunk be. Pldnk egyben a getline fggvny jabb vltozata is: tanulsgos lesz, ha sszehasonltjuk az 1. fejezetbeli vltozattal! #define MAXLINE 1000 main () /* Adott mintra illeszked sszes sor megkeresse*/

feladat

alapstruktrja

while (van mg sor) if (a sor tartalmazza a mintt)

88

{ char line [MAXLINE]; while (getline (line, MAXLINE) > 0) if (index (line, the) >= 0) printf(%s, line); } getline (s, lim) visszatrsi rtk a sorhosszsg*/ char s []; int lim; { int c, i; i = 0; while (--lim > 0 && (c = getchar ()) != EOF && c != \n) s [i++] = c; if (c == \n) s [i++] = c; s [i] = \0; return (i); } index (s, t) /*Visszaadja t s-beli indext; /*Sor beolvassa s-be,

1 , ha t nincs s-ben*/ char s [], t []; { int i, j, k; for (i = 0; s [i] != \0; i++) { for (j = i , k = 0; t [k] != \0 && s [j] == t [k];j++ , k++) ; if (t [k] == \0) return (i); } return (-1); } Minden fggvny az albbi alak : nv (argumentumlista, ha van) argumentumdeklarcik, ha vannak { deklarcik s utastsok, ha vannak } Mint lthat, fggvny : dummy () fggvny foglalni tpusnv rtkkel a klnfle rszek hinyozhatnak; a legrvidebb

{ } ami semmit sem csinl. (Az ilyen semmit sem csinl gyakran hasznos, ha a programfejleszts sorn le akarjuk egy ksbb megrand programrsz helyt.) A fggvnynevet is megelzheti, amennyiben a fggvny nem egsz tpus tr vissza; errl a kvetkez szakaszban lesz sz.

A program lnyegben egyedi fggvnydefincik halmaza. A fggvnyek kztti kommunikci (ebben az esetben) argumentumokkal s a fggvnyek ltal visszaadott rtkekkel trtnik, de trtnhet kls vltozkon keresztl is. A fggvnyek a forrsllomnyon bell tetszleges sorrendben fordulhatnak el, s a forrsprogram tbb llomnyra bonthat, csak fggvnyeket nem szabad kettvgni.

89

A hvott fggvny meghvjnak a return utasts segtsgvel adhat vissza rtket. A return utastst tetszleges kifejezs kvetheti: return (kifejezs) A hv fggvnynek jogban ll a visszaadott rtket figyelmen kvl hagyni. Nem szksges tovbb, hogy a return utn kifejezs lljon, ez esetben a hv nem kap vissza semmit. A vezrls akkor is rtk tadsa nlkl tr vissza a hvhoz, ha a vgrehajts a fggvny vgn elri a zr jobb oldali kapcsos zrjelet. Ez a megolds megengedett, de valsznleg valamilyen bajt jelez, ha a fggvny rtket ad vissza az egyik helyrl s nem ad rtket egy msikrl. Mindenesetre az olyan fggvny rtke, amely nem ad vissza rtket, bizonyosan rtelmetlen (hatrozatlan, hulladk). Az ilyen jelleg hibkat a C nyelv lint nev helyessgvizsgl programja jelzi. A tbb llomnyra tagold C programok fordtsnak s betltsnek mechanizmusa rendszerrl rendszerre vltozik. Az UNIX opercis rendszerben pl. az 1. fejezetben emltett cc parancs vgzi el ezt a feladatot. Tegyk fel, hogy a hrom fggvny hrom llomnyban tallhat, amelyeknek a neve main.c, getline.c s index.c. Ekkor a cc main.c getline.c index.c parancs lefordtja a hrom llomnyt, az eredmnyl kapott thelyezhet formtum trgykdot a main.o, getline.o s index.o nev llomnyokba helyezi, s betlti ket az a.out nev vgrehajthat llomnyba. Ha hiba fordul el, mondjuk a main.c-ben, akkor az illet llomny nmagban jrafordthat s az eredmny betlthet a korbban kapott llomnyokkal egytt a cc main.c getline.o index.o paranccsal. A cc parancs a .c, ill. az .o nvadsi konvencik segtsgvel klnbzteti meg a forrsllomnyokat (source) a trgykdot tartalmaz (object) llomnyoktl. 4.1. Gyakorlat. rjunk egy rindex(s, t) nev fggvnyt, amely t sbeli legjobboldalibb elfordulsnak pozcijt adja vissza, ill. -1-et ad, ha t nem fordul el s-ben! 4.2. Nemegsz tpus rtkekkel visszatr fggvnyek Idig egyetlen programunk sem tartalmazott a fggvny tpusra vonatkoz deklarcit. Ennek az az oka, hogy alaprtelmezs szerint a fggvnyek implicit mdon deklarltak azltal, hogy megjelennek valamely utastsban vagy kifejezsben, mint pl.: while (getline (line, MAXLINE) > 0) Ha valamely kifejezsben korbban mg nem deklarlt nv fordul el, amelyet bal oldali kerek zrjel kvet, akkor ezt a gp a szvegkrnyezet alapjn fggvnynvknt deklarlja. Ezenkvl alaprtelmezs szerint a fggvnyrl azt felttelezzk, hogy int tpus rtket ad vissza. Mivel a char kifejezsekben int mennyisgg alakul t, a char tpussal visszatr fggvnyeket sem kell deklarlni. Ezzel az esetek tbbsgt lefedtk, belertve sszes eddigi pldnkat is. Mi trtnik azonban, ha a fggvnynek valamilyen ms tpus rtket kell visszaadnia? Sok numerikus fggvny - mint pl. az sqrt, sin s cos - double tpus rtket ad vissza; ms specilis fggvnyek ms tpusokat. Ezek alkalmazst az atof(s) fggvnnyel szemlltetjk, amely az s karakterlncot a neki megfelel

90

duplapontossg lebegpontos szmm alaktja. Az atof az atoi kiterjesztse, amelynek tbb vltozatt is megrtuk a 2. s 3. fejezetben. Az atof kezeli az esetleges eljelet s tizedespontot, valamint a jelenlev vagy hinyz egsz, ill. trt rszt. (Ez azonban nem nevezhet j minsg bemeneti konverzis rutinnak; ilyen rutin megrsa tbb helyet ignyelne, mint amit most erre a clra sznunk.) Elszr is az atof maga kell, hogy deklarlja az ltala visszaadott rtk tpust, mivel az nem int. Tekintve, hogy kifejezsekben a float double mennyisgg alakul t, nincs rtelme azt mondanunk, hogy az atof float rtket ad vissza; jl kihasznlhatjuk a ktszeres pontossgot, s a fggvnyt double rtkkel visszatrnek deklarljuk. A tpus neve megelzi a fggvny nevt: double atof (s) /*Az s karakterlnc talaktsa double-l*/ char s []; { double val, power; int i, sign; for (i = 0; s [i] == || s [i] == \n || s [i] == \t; i++) ; /* res hely tugrsa*/ sign = 1; if (s [i] == + || s [i] == -)/*Eljel*/ sign = (s [i++] == +) ? 1 : -1; for (val = 0; s [i] >= 0 && s [i] <= 9; i++) val = 10 * val + s [i] - 0; if (s [i] == .) i++; for (power = 1 ; s [i] >= 0 && s [i] <= 9; i++) { val = 10 * val + s [i] 0; power *= 10; } return (sign * val / power); } Msodszor is, ugyanilyen fontos, hogy a hv rutinnak kzlnie kell, hogy az atof nemegsz rtket ad vissza. A deklarcit a kvetkez primitv kalkultor-program mutatja (a program pphogy elegend pl. egy csekknyv egyenlegnek kiszmtshoz). A program soronknt egyegy szmot olvas be, amelyet eljel elzhet meg, a szmokat sszeadja s az sszeget minden beolvass utn kinyomtatja: #define MAXLINE 100 main () /*Primitv kalkultor*/ { double sum, atof(); char line [MAXLINE]; sum = 0; while (getline (line, MAXLINE) > 0) printf (\t %.2f \n, sum += atof (line)); } A double sum, atof (); deklarci rtelmben sum double tpus vltoz, s atof olyan fggvny, amely double rtkkel tr vissza. Amennyiben atof nincs mindkt helyen explicit mdon deklarlva a C felttelezi, hogy egsz tpus rtkkel tr vissza, s gy rtelmetlen vlaszokat kapunk. Ha maga az atof s main-beli hvsa kvetkezetlen mdon fordul el ugyanabban a forrsllomnyban, ezt a

91

fordt szreveszi. Ha azonban az atof fggvnyt kln fordtottuk (ami valszn), az eltrst a gp nem veszi szre, az atof double rtket ad vissza, amit a main int rtkknt kezel, s rtelmetlen vlaszokat kapunk. (A lint kimutatja az ilyen hibt!) Az atof birtokban elvileg gy is (karakterlnc konvertlsa int-t): egsz szmm*/ char s []; { double atof (); return (atof (s)); } Figyeljk meg a deklarcik s a return utasts struktrjt. A kifejezs rtke a return (kifejezs) ben mindig olyan tpusv alakul t, mint amilyen a fggvny tpusa, mg mieltt a hvhoz val visszatrs megtrtnne. gy atof rtke, ami double, automatikusan int tpusv alakul t a return-ben val megjelenskor, mivel az atoi fggvny int rtkkel tr vissza. (A lebegpontos rtk int tpusv trtn konverzija levgja az esetleges trt rszt, amint errl a 2. fejezetben sz volt.) 4.2. Gyakorlat. Bvtsk ki atof-ot oly mdon, hogy az 123.45e-6 alak tudomnyos jellsmdot is kezelni tudja, ahol a lebegpontos szmot e vagy E s egy esetleges eljellel elltott kitev kvetheti! 4.3. Tovbbi tudnivalk a fggvnyargumentumokrl Az 1. fejezetben megtrgyaltuk a nyelvnek azt a tulajdonsgt, hogy a fggvnyargumentumok rtk szerint addnak t, vagyis a hvott fggvny az egyes argumentumoknak nem a cmt, hanem a kln ideiglenes msolatt kapja meg. Eszerint a fggvny nem kpes befolysolni a hv fggvnyben tallhat eredeti argumentumot. A fggvnyen bell valjbanminden argumentum loklis vltoz, amely azzal az rtkkel inicializldott, amivel a fggvnyt meghvtk. Ha a fggvny argumentumaknt tmbnv jelenik meg, a tmb kezdcme addik t; a tmbelemek nem msoldnak t. A fggvny az tadott cmtl kezdd indexelssel megvltoztathatja a tmb elemeit. A tmbk teht nv szerint addnak t. Az 5. fejezetben elmondjuk, hogyan lehet a mutatkat gy hasznlni, hogy a hv fggvnyekben tallhat nemtmb jelleg vltozkat is befolysolni tudjuk. Megjegyezzk, hogy nincs teljesen kielgt mdszer olyan gpfggetlen fggvnyek rsra, amelyek vltoz szm argumentumot fogadnak. Nincs ugyanis olyan gpfggetlen eljrs, amellyel a hvott fggvny meg tudn hatrozni, hogy adott hvs alkalmval tnylegesen hny argumentumot kapott. gy nem tudunk pldul olyan, igazn gpfggetlen programot rni, amely ki tudn vlasztani tetszleges szm argumentum kzl a legnagyobbat, amint azt a FORTRAN s a PL/1 max nev beptett fggvnye teszi. Vltoz szm argumentum ltalban ha a hvott fggvny nem hasznl tnylegesen nem kapott meg, tovbb kvetkezetes. A printf, amely a biztonsgosan hasznlhat, olyan argumentumot, amit ha a tpusok hasznlata legkznsgesebb vltozmegrhatjuk az atoi fggvnyt

atoi (s) /*Az s karakterlnc talaktsa

92

argumentumszm C fggvny, az els argumentumban tallhat informci alapjn hatrozza meg, hogy mg hny argumentum kvetkezik s azoknak mi a tpusa. Slyos hiba lp fel, ha a hv nem ad elegend szm argumentumot, vagy ha a tpusok nem azonosak azzal, amit az 1. argumentum mond. A printf sem gpfggetlen, s klnbz krnyezetek esetben mdostani kell. Msik lehetsg, hogy amennyiben az argumentumok ismert tpusak, valamilyen megllapods szerint, pl. egy specilis argumentumrtkkel (ami gyakran a nulla) meg lehet jellni az argumentumlista vgt. 4.4. Kls vltozk A C program kls objektumok halmaza. Ezek vltozk vagy fggvnyek lehetnek. A kls jelzt a bels fogalommal val szembellts kedvrt hasznljuk, amely utbbi a fggvnyeken bell definilt argumentumokat s automatikus vltozkat rja le. A kls vltozkat fggvnyeken kvl definiljuk, gy sok fggvny szmra elrhetk. Maguk a fggvnyek mindig klsk, mivel a C-ben nem lehet fggvnyeket ms fggvnyeken bell definilni. Megllapods szerint a kls vltozk egyben globlis vltozk is teht minden, az ilyen vltozra ugyanazzal a nvvel trtn hivatkozs (mg a teljesen kln fordtott fggvnyekbl is) ugyanarra a fizikai objektumra trtn hivatkozst jelent. Ebben az rtelemben a kls vltozk a FORTRAN vagy PL/1 externaljainak felelnek meg. Ksbb ltni fogjuk, hogyan definilhatunk olyan kls vltozkat s fggvnyeket, amelyek globlisan nem hozzfrhetk, hanem csupn egyetlen forrsllomnyon bell lthatk. Mivel a kls vltozk globlisan hozzfrhetk, helyettesthetik a fggvnyargumentumokat s a fggvnyek kztti kommunikci cljait szolgl visszatrsi rtkeket. Brmelyik fggvny hozzfrhet kls vltozhoz az illet vltoz nevre trtn hivatkozssal, ha a nevet korbban deklarltk. Ha fggvnyek kztt nagy szm vltozt kell megosztani, a kls vltozk hasznlata knyelmesebb s hatkonyabb, mint a hossz argumentumlistk. Amint az 1. fejezetben rmutattunk, ezt az okoskodst fenntartssal kell fogadnunk, mivel az ilyen megolds rontja a program ttekinthetsgt s olyan programokat eredmnyez, amelyekben sok a fggvnyek kztti adatkapcsolat. A kls vltozk hasznlatnak msodik oka az inicializlssal kapcsolatos. Klnsen lnyeges, hogy a kls tmbk inicializlhatk, az automatikus tmbk azonban nem. E fejezet vge fel foglalkozunk az inicializlssal. A harmadik ok - ami miatt kls vltozkat hasznlunk rvnyessgi tartomnyuk s fennmaradsi idejk. Az automatikus vltozk valamely fggvnyre nzve bels vltozk : akkor jnnek ltre, amikor a vezrls belp a rutinba, s megsznnek az onnan val kilpskor. A kls vltozk viszont llandan megmaradnak: nem jnnek-mennek, gy az egyik fggvnyhvstl a msikig megtartjk rtkket. Ha teht kt fggvnynek meg kell osztoznia valamilyen adathalmazon s egyik fggvny sem hvja a msikat, gyakran az a legknyelmesebb, ha a kzsen hasznlt adatokat kls vltozkban tartjuk s nem adogatjuk t ide-oda argumentumokon keresztl. Vizsgljuk tovbb ezt a krdst egy nagyobb pldn keresztl!

93

A feladat egy jabb, az elznl jobb kalkultorprogram rsa. Ez a program mr megengedi a +, -, * , / s = mveleteket. A kalkultor az infix jellsmd helyett a fordtott lengyel (reverse Polish) jellsmdot hasznlja, mivel az utbbi knyelmesebb. (gy mkdnek pl. a Hewlett Packard gyrtmny zsebszmolgpek.) Ebben a jellsmdban minden opertor az operandusai utn ll; az olyan infix kifejezst, mint pl. (1 - 2) * (4 + 5) = gy rjuk be, hogy: 1 2 - 4 5 + * = Zrjelekre nincs szksg. A megvalsts egszen egyszer. Minden operandust egy verembe tolunk; opertor rkezsekor a megfelel darabszm operandus (ktoperandus opertorok esetben kett) kilp a verembl, elvgezzk rajtuk az opertor ltal meghatrozott mveletet, majd az eredmnyt ismt visszarjuk a verembe. A fenti esetben pl. elbb 1 s 2 a verembe kerl, majd a helykbe a kett klnbsgt, vagyis -1-et rjuk. Ezutn 4-et s 5-t toljuk a verembe, amelyeket azutn az sszegk, vagyis 9 helyettest. Vgl a szorzs utn -1 s 9 helyre szorzatuk, vagyis -9 kerl a verembe. Az = opertorral kinyomtatjuk a verem legfels elemt anlkl, hogy onnan elmozdulna (gy egy szmts rszeredmnyei is ellenrizhetk). Br a verembe tols s az onnan trtn kilptets (push s pop) mveletei egyszerek, mire a hibafigyelst s javtst is hozzfzzk, elg hossz programot kapunk ahhoz, hogy mindent kln fggvnybe tegynk, ahelyett, hogy ugyanazt a programkdot ismtelgetnnk az egsz programon keresztl. Szksg van tovbb egy kln fggvnyre, amely beolvassa a kvetkez bemen opertort vagy operandust. gy a program felptse: while (a kvetkez opertor vagy operandus nem az llomny vge) if (szm) told a verembe else if (opertor) lptesd ki az operandusokat vgezd el a mveletet told a verembe az eredmnyt else hiba Nem dntttnk mg a f krdsben - hol legyen a verem, vagyis mely rutinok frhessenek hozz kzvetlenl. Az egyik lehetsg, hogy a vermet a main rutinban tartjuk, s a vermet s a pillanatnyi verempozcit tadjuk a verembe rst s az onnan trtn kilptetst vgz rutinoknak. A main rutinnak azonban nem kell tudnia a vermet vezrl vltozkrl; csupn a verembe trtn rsra s az onnan trtn kilptetsre kell gyelnie. Ezrt gy dntttnk, hogy a vermet s a hozz kapcsold informcit olyan klsvltozkkal brzoljuk, amelyekhez a push s pop fggvnyek hozzfrhetnek, a main azonban nem. Ezt a megoldst egyszeren lefordthatjuk a programozs nyelvre. A fprogram lnyegben az opertorok s operandusok tpusra vonatkoz nagy switch-bl ll, ez taln tipikusabb hasznlata a switch utastsnak, mint amit a 3. fejezetben lttunk: #define MAXOP 20 /*Operandus s opertor max.mrete*/ #define NUMBER 0 /*Szm szlelsnek jelzse*/ #define TOOBIG 9 /*Jelzi, hogy a karakterlnc tl nagy*/

94

main () /*Fordtott lengyel logikj kalkultor*/ { int type; char s [MAXOP]; double op2, atof(), pop(), push(); while ((type = getop (s, MAXOP)) != EOF) switch (type) { case NUMBER: push (atof(s)); break; case + : push (pop() + pop()); break; case * : push (pop() * pop()); break; case - : op2 = pop (); push (pop() - op2); break; case /: op2 = pop (); if (op2 != 0.0) push (pop () / op2); else printf (az oszt nulla\n); break; case =: printf (\t %f \n, push(pop())); break; case c: clear (); break; case TOOBIG: printf (%.20s . . .tl hossz\n, s); break; default: printf (ismeretlen parancs %c \n, type); break; } } #define MAXVAL 100 /*rtkverem max. mlysge*/ int sp = 0; /*Veremmutat*/ double val [MAXVAL]; /* rtkverem*/ double push (f) double f; /*f rsa az rtkverembe*/

{ if (sp < MAXVAL) return (val [sp++] = f); else { printf (hiba: a verem megtelt\n); clear (); return (0); } } double pop () /*A legfels rtk kiemelse a verembl*/

{ if (sp > 0) return (val [--sp]); else { printf (hiba: a verem res\n); clear (); return (0); } } clear () /*A verem kirtse*/ { sp = 0; } A c parancs annak a clear fggvnynek a segtsgvel rti ki a vermet, amit hiba esetn a push s a pop is hasznl. A getop fggvnnyel rvidesen foglalkozunk.

95

Amint arrl az 1. fejezetben mr sz volt, egy vltoz akkor kls, ha az sszes fggvny trzsn kvl definiljuk. gy a push, a pop s a clear ltal hasznlt vermet s veremmutatt e hrom fggvnyen kvl definiltuk. Maga a main azonban nem hivatkozik a veremre s a veremmutatra - a verem brzolst gondosan elrejtettk. gy az = opertorra vonatkoz programrsznek a push (pop ()); utastst kell hasznlnia ahhoz, hogy a verem tetejt a verem megvltoztatsa nlkl meg lehessen vizsglni. Figyeljk meg tovbb, hogy mivel a + s a * kommutatv opertorok, a kilptetett operandusok kombinlsnak sorrendje kzmbs, a - s a / opertorok esetben azonban meg kell klnbztetni a bal oldali s a jobb oldali operandust. 4.3. Gyakorlat. Az alapvet programkeret megtartsval egyszeren kibvthetjk a kalkultorprogramot. Vezessk be a modul (_%) s az egyoperandus mnusz opertorokat! Vezessk be tovbb az erase parancsot, amely trli a verem legfels elemt! Vezessnk be vltoznevek kezelst lehetv tev parancsokat! (A huszonhat egybets vltoznvre egyszeren megoldhat.) 4.5. Az rvnyessgi tartomny szablyai Nem szksges egyszerre lefordtani a C programot alkot sszes fggvnyt s kls vltozt: a program forrsszvege tbb llomnyban trolhat, s knyvtrakbl mr elzleg lefordtott rutinok is betlthetk. Ezzel kapcsolatban kt rdekes krds merl fel: Hogyan lehet a deklarcikat gy megrni, hogy a fordts sorn a vltozk helyesen deklarldjanak? Hogyan kell elkszteni a deklarcikat ahhoz, hogy a program betltsekor az sszes rszlet helyesen kapcsoldjon ssze? Egy nv rvnyessgi tartomnya a programnak az a rsze, amelyre vonatkozan a nevet definiltuk. A fggvny elejn definilt automatikus vltoz rvnyessgi tartomnya az a fggvny, amelyben a nevet deklarltuk, s a ms fggvnyekben ugyanilyen nven ltez vltozkat ez nem rinti. Ugyanez igaz a fggvny argumentumaira. A kls vltoz rvnyessgi tartomnya ott kezddik, ahol a forrsllomnyban a vltozt deklarltuk s az illet llomny vgig tart. Ha pl. a val, sp, push, pop s clear ebben a sorrendben, egyetlen llomnyban vannak definilva, vagyis: int sp = 0; double val [MAXVAL]; double push (f) { . . . } double pop () { . . . } clear () { . . . } akkor a val s sp vltozk a push, pop s clear fggvnyekben egyszeren megnevezskkel hasznlhatk, s nincs szksg tovbbi deklarcikra. Ha viszont egy kls vltozra mg annak definilsa eltt kell hivatkozni, vagy ha egy kls vltozt ms forrsllomnyban definilunk, mint ahol hasznlunk, akkor ktelezen extern deklarcit kell alkalmazni. Lnyeges, hogy klnbsget tegynk valamely kls vltoz deklarcija s defincija kztt! A deklarci a vltoz tulajdonsgait rja le (tpust, mrett stb.), mg a defincival trterletet is lefoglalunk. Ha az int sp; double val [MAXVAL];

96

sorok minden fggvnyen kvl jelennek meg, akkor definiljk az sp s val nev kls vltozkat, trterletet foglalnak le, s az adott forrsllomny tbbi rsze szmra deklarciknt is szolglnak. Msrszt az extern int sp; extern double val []; sorok deklarljk, hogy sp int tpus, val pedig double tpus tmb (amelynek mrett mshol hatrozzuk meg), de ezek a sorok nem hozzk ltre a vltozkat s nem foglalnak le szmukra trterletet. A forrsprogramot alkot llomnyok kztt csupn egyben kell a kls vltoz defincijnak szerepelnie; a tbbi llomnyban extern deklarcival biztostjuk a vltoz elrst. (A defincit tartalmaz llomnyban is lehet extern deklarci.) Kls vltozt csak definilskor lehet inicializlni. A tmbmreteket a definciban kell megadni, de opcionlisan extern deklarciban is szerepelhetnek. Br az elbbi programban az ilyenfajta szervezs nem valszn, elkpzelhet, hogy a val s sp vltozkat az egyik llomnyban definiljuk s inicializljuk, mg a push, pop s clear fggvnyeket egy msikban. Ekkor sszekapcsolsukhoz a kvetkez defincik s deklarcik szksgesek: Az 1. llomnyban: int sp = 0; /* Veremmutat*/ double val [MAXVAL]; /* rtkverem*/ A 2. llomnyban: extern int sp; extern double val []; double push (f) { . . . } double pop () { . . . } clear () { . . . } Minthogy a 2. llomnyban tallhat extern deklarcik a hrom fggvny eltt s azokon kvl fordulnak el, ezrt mindegyikkre vonatkoznak, teht egyetlen deklarcikszlet elegend lesz az egsz 2. llomnyhoz. Fejezetnkben sz lesz mg a nagyobb programoknl elnys #include szolgltatsrl, amely lehetv teszi, hogy csak egyszer rjuk le az extern deklarcikat, amelyek azutn fordts kzben minden forrsllomnyba beillesztdnek. Nzzk most a getop megvalstst, amely a kvetkez opertort vagy operandust olvassa be. Az alapfeladat egyszer: a szkzk, tabok s jsorok tugrsa. Ha a kvetkez karakter nem szmjegy s nem tizedespont, akkor getop visszaadja az illet karaktert. Egybknt sszegyjti a szmjegyekbl ll karakterlncot (amely tizedespontot is tartalmazhat) s NUMBERrel tr vissza, jelezve, hogy a bemenetre szm rkezett. A rutin elg bonyolult, mivel arra trekedtnk, hogy azt az esetet is helyesen kezelje, amikor a beolvasott szm tl hossz. A getop mindaddig szmjegyeket olvas be (esetleg kzben egy tizedespontot is), amg azok el nem fogynak, de csupn azokat trolja, amelyek elfrnek. Ha nem volt tlcsorduls, akkor NUMBER-rel s a szmjegyek karakterlncval tr vissza. Ha azonban a szm tl hossz volt, akkor figyelmen kvl hagyja a beolvasott sor htralev rszt, s gy a felhasznl a hiba helytl kezdve egyszeren jrarhatja a sort. A fggvny a tlcsordulst a TOOBIG-gel val visszatrssel jelzi: getop (s, lim) /*A kv. opertor vagy operandus beolvassa*/ char s []; int lim; {

97

int i, c; while ((c = getch()) == || c == \t || c == \n) ; if (c != . && (c < 0 || c > 9)) return ; s [0] = c; for (i = 1; (c = getchar()) >= 0 && c <= 9; i++) if (i < lim) s [i] = c; if (c == .) { /*A trt rsz beolvassa*/ if (i < lim) s [i] = c; for (i++; (c = getchar()) >= 0 && c <= 9; i++) if (i < lim) s [i] = c; } if (i < lim) { /*A szm rendben van*/ ungetch; s [i] = \0; return (NUMBER); } else { /*Tl nagy, a sor tbbi rszt tugorja*/ while (c != \n && c != EOF) c = getchar(); s [lim - 1] = \0; return (TOOBIG); } } Mit jelent getch s ungetch? Gyakran az a helyzet, hogy a bemenetet olvas program csak akkor jn r, hogy eleget olvasott, amikor mr a kelletnl tbb karaktert olvasott be. Ilyen eset pl., amikor egy szmot alkot karaktereket kell beolvasni: amg a program nem szleli az els nem-szmjegyet, a szm nem teljes. Ehhez azonban a programnak a szksgesnl eggyel tbb karaktert kell beolvasnia, egy olyan karaktert, amelyre nincs felkszlve. Valahogy teht nembeolvasott kellene tenni a nemkvnt karaktert. Amikor a program a szksgesnl eggyel tbb karaktert olvasott be, vissza kellene helyezni azt a bemenetre, gy a program a tovbbiakban gy viselkedhetne, mintha ezt a felesleges karaktert sohasem olvasta volna be. Szerencsre mindezt kt, egymssal egyttmkd fggvny megrsval knnyen megoldhatjuk. getch szlltja a kvetkez megvizsgland bejv karaktert; ungetch visszar egy karaktert a bemenetre oly mdon, hogy a kvetkez getch hvs ismt ezt a karaktert szolgltatja. Az egyttmkds mdja egyszer. Az ungetch a felesleges karaktereket egy megosztott pufferbe egy karaktertmbbe - rja vissza. A getch kiolvassa a puffert, amennyiben abban van valami, ill. ha res, meghvja a getchar fggvnyt. Szksg van egy olyan indexvltozra is, amely az ppen vizsglt karakter pufferbeli pozcijt mutatja. Mivel a getch s az ungetch a puffert s az indexet kzsen hasznlja, az utbbiaknak a hvsok kztt meg kell tartaniuk rtkket, mindkt rutinra nzve kls vltozknak kell lennik. gy a getch, ungetch s az ltaluk megosztva hasznlt vltozk az albbi mdon rhatk: #define BUFSIZE 100 char buf [BUFSIZE]; /*Az ungetch puffere*/ int bufp = 0; /*A kvetkez szabad pozci buf-ban*/ getch () /*Kiolvas egy (esetleg visszart) karaktert*/ { return ((bufp > 0) ? buf [--bufp] : getchar());

98

} ungetch /* Karakter visszahelyezse a bemenetre*/ int c; { if (bufp > BUFSIZE) printf(ungetch, tlsok karakter\n); else buf [bufp++] = c; } A pufferbe trtn visszarsra egyetlen karakter helyett tmbt hasznltunk, mivel ez az ltalnosts a ksbbiekben mg jl jhet. 4.4. Gyakorlat. rjuk meg az ungets(s) nev rutint, amely egy teljes karakterlncot r vissza a bemenetre! Szksges, hogy az ungets fggvnynek tudomsa legyen buf-rl s bufp-rl, vagy csak egyszeren hasznlja az ungetch fggvnyt? 4.5. Gyakorlat. Tegyk fel, hogy sohasem helyeznk vissza egynl tbb karaktert. Mdostsuk a getch s ungetch fggvnyeket ennek megfelelen ! 4.6. Gyakorlat. getch s ungetch rutinjainak a visszahelyezett EOFot gpfgg mdon kezelik. Hatrozzuk meg, hogyan viselkedjenek rutinjaink, amikor EOF-ot runk vissza, majd valstsuk meg ezt a megoldst! 4.6. Statikus vltozk A mr korbban megismert extern s automatikus vltozk mellett a statikus (static) vltozk jelentik a harmadik trolsi osztlyt. A static vltozk akr belsk, akr klsk lehetnek. A bels static vltozk ugyangy loklisak valamely fggvnyre nzve, mint az automatikus vltozk, de az automatikusaktl eltren llandan fennmaradnak s nem jnnek ltre, ill. sznnek meg a fggvny minden egyes aktivizlsa alkalmval. Eszerint a bels static vltozk a fggvnyen bell sajt, lland trat kpeznek. A fggvnyeken bell megjelen karakterlncok, mint pl. a printf argumentumai, bels static vltozk. A kls static vltoz annak a forrsllomnynak a tovbbi rszben lesz ismert, amelyben deklarltk, de rvnyessgi tartomnya nem terjed ki egyetlen ms llomnyra sem. A kls static vltozk segtsgvel lehetsgnk van arra, hogy az olyan neveket mint buf s bufp elrejtsk a getch-ungetch kombinciban. A vltozknak klsknek kell lennik ahhoz, hogy megoszthatk legyenek, ugyanakkor rejtve kell maradniuk a fggvnyek felhasznli ell, mivel gy kizrjuk a konfliktus lehetsgt. Ha a kt rutint s a kt vltozt egyetlen llomnyba szerkesztjk: static char buf [BUFSIZE]; /*Az ungetch puffere*/ static int bufp = 0 /*A kvetkez szabad pozci buf-ban*/ getch () { . . . } ungetch { . . . } akkor egyetlen ms rutin sem frhet hozz a buf s bufp vltozkhoz; de nem kerlhetnek sszetkzsbe a vltozk az ugyanezen program ms llomnyaiban elfordul ugyanilyen nevekkel sem. A statikus trolst, legyen az akr bels, akr kls, gy definiljuk, hogy a kznsges deklarci el a static szt rjuk. A vltoz kls, ha az sszes fggvnyen kvl, ill. bels, ha valamelyik fggvnyen bell definiljk. A fggvnyek ltalban kls objektumok, a nevk globlisan ismert. Ugyanakkor a fggvnyek static tpusnak is deklarlhatk, az ilyen

99

fggvnyek neve deklarltk.

ismeretlen

lesz

azon

az

llomnyon

kvl,

ahol

A C nyelvben a static deklarci nem csupn llandsgot rejt magban, hanem bizonyos mrtk elzrtsgot is. A bels static objektumok csupn az adott fggvnyen bell ismertek; a kls static objektumok (vltozk vagy fggvnyek) pedig csak abbl a forrsllomnybl hozzfrhetk, amelyben megjelennek, s neveik nem kerlnek sszetkzsbe a ms llomnyokban elfordul ugyanilyen nev vltozkkal, ill. fggvnyekkel. Kls static vltozk s fggvnyek segtsgvel elrejthetjk az adatobjektumokat s a velk dolgoz bels rutinokat, gy ms rutinok s adatok ezekkel mg vletlenl sem kerlhetnek sszetkzsbe. A getch s az ungetch fggvny pl. karakterbeolvas_ s -visszar modult alkot; buf s bufp pedig static kell, hogy legyen ahhoz, hogy kvlrl ne legyen elrhet. Hasonlkppen push, pop s clear egy veremkezel modult alkot; val-nak s sp-nek ugyancsak kls staticnak kell lennie. 4.7. Regisztervltozk A negyedik s egyben utols trolsi osztly neve register. A register deklarci kzli a fordtprogrammal, hogy a krdses vltozra nagyon gyakran trtnik hivatkozs. Amennyiben lehetsges, a register tpus vltozk a gp regisztereibe kerlnek, miltal rvidebb s gyorsabb programokjnnek ltre. A register deklarci alakja: register int x; register char c; s gy tovbb; az int rsz elhagyhat. A register deklarci csupn automatikus vltozkra, valamint fggvnyek formlis paramtereire alkalmazhat. Utbbi esetben a deklarci alakja: f (c, n) register int c, n; { register int i; . . . } A gyakorlatban a regisztervltozkra nzve olyan megszortsok llnak fenn, amelyek az adott hardver tulajdonsgait tkrzik. Az egyes fggvnyeknek csupn nhny vltozja trolhat regiszterekben s csak bizonyos tpusok megengedettek. A fls szm, ill. meg nem engedett deklarcik esetben a register szt a gp figyelmen kvl hagyja. Nem hivatkozhatunk tovbb valamely regisztervltoz cmre (ezzel a tmval az 5. fejezetben foglalkozunk). A specilis megktsek gprl gpre vltoznak: pldul a PDP- 11 szmtgpen csupn a fggvnyen belli els hrom regiszterdeklarci hatsos, a tpus pedig int, char vagy mutat lehet. 4.8. Blokkstruktra A PL/1, ill. az ALGOL rtelmben a C nem blokkstruktrlt nyelv, amennyiben fggvnyek nem definilhatk ms fggvnyek belsejben. Vltozkat azonban definilhatunk blokkstruktrlt mdon. Nyit kapcsos zrjel utn - amely nemcsak fggvny, hanem mindenfajta sszetett utasts kezdett jelzi vltozdeklarcik s inicializlsok egyarnt llhatnak. Az ily mdon deklarlt vltozk fellbrljk a klsbb blokkokban ugyanilyen nv alatt elfordul vltozkat s a vonatkoz jobb

100

oldali kapcsos zrjelig rvnyben maradnak. Pl. az if (n > 0) { int i; /* j i deklarlsa*/ for (i = 0; i < n; i++) . . . } programban az i vltoz rvnyessgi tartomnya az if utasts igaz ga; ennek az i-nek semmi kze a programban elfordul brmely egyb i-hez. A blokkstruktra kls vltozkra is alkalmazhat. Ha adottak az int x; f () { double x; . . . } deklarcik, akkor az f fggvnyen bell az x elfordulsai a bels, double tpus vltozra, f-en kvl a kls int vltozra vonatkoznak. Ugyanez igaz a formlis paramterek neveire : int z; f (z) double z; { . . . } Az f fggvnyen vonatkozik. bell z a formlis s nem a kls paramterre

4.9. Inicializls Az inicializlsrl futlag mr tbbszr is szltunk, de mindig csak mellkesen valamely ms tma kapcsn. Ebben a fejezetben sszefoglaljuk a szablyok egy rszt, miutn mr megtrgyaltuk a klnfle trolsi osztlyokat. Explicit inicializls hinyban a kls s a statikus vltozk kezdeti rtke garantltan nulla lesz; az automatikus s a regisztervltozk rtke hatrozatlan. Az egyszer vltozk (nem a tmbk s a struktrk) a deklarlsukkor inicializlhatk oly mdon, hogy a nevket egyenlsgjel s egy lland kifejezs kveti : int x = 1; char squote = \; /*Aposztrf*/ long day = 60 * 24; /*Percek szma a napban*/ Kls s statikus vltozk esetben az inicializls egyszer, mgpedig rtelemszeren a fordtsi idben trtnik meg. Az automatikus s a regisztervltozk minden alkalommal inicializldnak, amikor a vezrls belp a fggvnybe vagy blokkba. Automatikus s regisztervltozk esetben az inicializls jobb oldaln nemcsak egy lland llhat - tetszleges, korbban definilt rtkeket, akr fggvnyhvsokat tartalmaz kifejezs is megengedett. A 3. fejezetben emltett binris keresprogram kezdeti rtk belltsai pl. a kvetkez mdon rhatk : binary (x, v, n) int x, v [], n; { int low = 0;

101

int high = n-1; int mid; . . . } a mr ltott: binary (x, v, n) int x, v [], n; { int low, high, mid; low = 0; high = n - 1; . . . } alak helyett. Az automatikus vltozk inicializlsai valjban rtkad utastsok rvidtett formi. Lnyegben csupn zls krdse, hogy valaki melyik alakot rszesti elnyben. ltalban explicit rtkadsokat hasznltunk, mivel a deklarcikban elfordul inicializlsok nehezebben kvethetk. Automatikus tmbk nem inicializlhatk. Kls s statikus tmbk gy inicializlhatk, hogy a deklarcit a kezdeti rtkek kapcsos zrjelek kz zrt s vesszkkel elvlasztott listja kveti. Az 1. fejezetben ismertetett karakterszmll program, amelynek kezdete main () /*Szmjegyek, res kzk s egyebek szmllsa*/ { int c, i, nwhite, nother; int ndigit [10]; nwhite = nother = 0; for (i = 0; i < 10; i++) ndigit [i] = 0; . . . } volt, ehelyett gy is rhat: int nwhite = 0; int nother = 0; int ndigit [10] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, } ; main () { int c, i; . . . } Az adott esetben ezek az inicializlsok szksgtelenek, mivel mindegyik kezdeti rtk nulla, ennek ellenre clszer explicit alakban megadni ket. Ha a megadott mretnl kevesebb szm kezdeti rtk van, akkor a tbbi kezdeti rtk nulla lesz. Tl sok kezdeti rtk megadsa hibt jelent. Sajnos nincs lehetsg valamely kezdeti rtk ismtldsnek megadsra, sem pedig arra, hogy a tmb valamely kzbens elemt az sszes tbbi kezdeti rtk megadsa nlkl inicializljuk. /*Szmjegyek, res kzk s egyebek szmllsa*/

102

A karaktertmbk a kezdetirtk-bellts specilis esett jelentik: a kapcsos zrjelekkel s vesszkkel trtn jellsmd helyett karakterlnc is hasznlhat : char pattern [] = the; Ez rvidtse a hosszabb, de ezzel egyenrtk rsmdnak: char pattern [] = { t, h, e, \0}; Ha egy - tetszleges tpus - tmb mrett elhagyjuk, a fordt a tmbhosszsgot a megadott kezdeti rtkek darabszmbl szmtja ki. A fenti esetben a mret ngy (hrom karakter s a zr \0). 4.10. Rekurzi A C megengedi a fggvnyek rekurzv hasznlatt, vagyis a fggvnyek (kzvetlenl vagy kzvetve) sajt magukat is hvhatjk. Ennek hagyomnyos pldja valamely szmnak karakterlncknt trtn nyomtatsa. Amint korbban emltettk, a szmjegyek rossz sorrendben generldnak: a kis helyirtk szmjegyek a nagyobb helyirtk szmjegyek eltt jnnek ltre, de a nyomtats fordtott sorrendben kell, hogy trtnjk. A problmnak kt megoldsa van. Az egyik megolds szerint a szmjegyeket a generls sorrendjben troljuk egy tmbben, majd fordtott sorrendben nyomtatjuk ki ket, ahogy ezt a 3. fejezetben az itoa fggvny tette. A printd els vltozata ezt a megoldst kveti. printd (n) /*n nyomtatsa decimlis alakban*/ int n; { char s [10]; int i; if (n < 0) { putchar (-); n = -n; } i = 0; do { s [i++] = n % 10 + 0; karaktert*/ } while ((n /= 10) > 0); while (--i >= 0) putchar (s [i]); }

/*Veszi a kvetkez /*Elhagyja*/

A msik lehetsg a rekurzv megolds, amelyben printd hvsakor elszr sajt magt hvja meg - feldolgozza az vezet szmjegyet, majd kinyomtatja az utols szmjegyet. printd (n) int n; { int i; if (n < 0) { putchar (-); n = -n; } if ((i = n / 10) != 0) printd (i); putchar (n % 10 + 0); } /*n nyomtatsa decimlis alakban (rekurzv)*/

minden sszes

103

Amikor a fggvny nmagt rekurzv mdon meghvja, minden hvs az automatikus vltozk friss kszlett kapja meg, fggetlenl a korbbi kszlettl. gy printd(123) esetben az els printd fggvnyben n = 123. tadja a 12-t az jabb printd fggvnynek, s 3-at nyomtat ki annak visszatrse utn. Ugyangy a msodik printd 1 -et ad t a harmadiknak (amely kinyomtatja), majd kirja a 2-t. A rekurzi ltalban nem gyorsabb, s tr-megtakartst sem jelent, mivel valahol ltre kell hozni egy vermet a feldolgozott rtkek szmra. A rekurzv programkd azonban tmrebb s gyakran knnyebben lerhat s megrthet. Mint a 6. fejezetben ltni fogjuk, a rekurzi klnsen knyelmes a rekurzv mdon definilt adatstruktrk, pl. a fastruktrk esetben. 4.7. Gyakorlat. A printd-ben alkalmazott megoldsok felhasznlsval rjuk meg az itoa rekurzv vltozatt, vagyis rekurzv rutin segtsgvel konvertljunk egy egsz szmot karakterlncc! 4.8. Gyakorlat. rjuk meg az s karakterlncot megfordt reverse(s) fggvny rekurzv vltozatt! 4.11. A C elfeldolgoz A C nyelv egy egyszer makro-elfeldolgoz (preprocesszor) segtsgvel bizonyos nyelvi kiterjesztseket is nyjt szmunkra. E kiterjesztsek kzl a legkznsgesebb a mr ltott #define, de ide tartozik az a nyelvi eszkz is, amely llomnyok tartalmnak a fordts sorn trtn beiktatst teszi lehetv. llomnyok beiktatsa A #define szimblumok, deklarcik s ms nyelvi objektumok kezelst is megknnyti a C llomnybeiktatsi szolgltatsa. Minden sor, amelynek alakja: #include llomnynv az llomnynv nev llomny tartalmval helyettestdik. (Az idzjelek ktelezk!) Gyakran minden forrsllomny elejn megjelenik egy vagy kt ilyen alak sor, amely a kzs #define utastsoknak s a globlis vltozk extern _deklarciinak beiktatsra szolgl. A #include parancsok egymsba skatulyzhatk. Az #include a legelnysebb mdja annak, hogy egy nagy mret program deklarciit sszegyjtsk. E megolds eredmnyekppen az sszes forrsllomny ugyanazokat a defincikat s vltozdeklarcikat kapja meg, s ezltal klnsen kellemetlen hibkat kerlhetnk el. Termszetesen olyankor, amikor valamelyik beiktatott llomny megvltozik, az sszes ettl fgg llomnyt jra le kell fordtani. Makrohelyettests A #define YES 1 alak defincival a legegyszerbb tpus makrohelyettestst valstjuk meg - egy nevet egy karakterlnccal helyettestnk. A #define-ban elfordul nevek alakja azonos a C azonostkval; a helyettest szveg tetszleges. A helyettest szveg ltalban a sor tovbbi rsze; hossz defincik gy folytathatk, hogy a folytatand sor vgre \-t helyeznk el. A #define szimblummal definilt nv rvnyessgi tartomnya a definci helytl az adott forrsllomny vgig tart. A nevek jradefinilhatk; a defincik korbbi defincikat hasznlhatnak. Idzjelek kz

104

rt karakterlncokra nzve, teht amikor pl. YES definilt nv, a printf(YES) ben nem trtnik helyettests. Minthogy a #define-t egy makro-elfeldolgoz, nem pedig maga a fordt dolgozza fel a defincikra csak nagyon kevs nyelvtani megkts vonatkozik. Az ALGOL hvei pl. a #define then #define begin { #define end; } defincik utn azt rhatjk, hogy: if (i > 0) then begin a = 1; b = 2; end Lehetsg van argumentumokkal rendelkez makrk definilsra, amikor is a helyettest szveg a makr hvsnak mdjtl fgg. Pldaknt definiljuk a max nev makrt az albbi mdon : #define max(A, B) ((A) > (B) ? (A) : (B)) Ekkor az x = max(p+q, r+s); sort az x =((p + q) > (r + s) ? (p + q) : (r + s)); sor fogja helyettesteni. Olyan maximum fggvnyt kaptunk teht, amelynek nem fggvnyhvs, hanem soron belli programkd a kifejtse. Ha az argumentumokat kvetkezetesen kezeljk, ez a makro brmilyen adattpusra meg fog felelni: nem szksges klnfle max-okat ksztennk a klnbz adattpusokra, mint ahogy azt fggvnyhvsok esetben tennnk. Amennyiben kzelebbrl megvizsgljuk a max elz kifejtst, tallhatunk benne nhny buktatt. A kifejezsek ktszer rtkeldnek ki; ez nem j olyankor, amikor mellkhatsuk pl. fggvnyhvs vagy inkrementl opertorok alkalmazsa. gyelnnk kell tovbb a zrjelek hasznlatra, nehogy megvltozzon a kirtkels sorrendje! (tekintsk a #define square(x) x * x makrt, amikor azt square(z + 1) alakban hvjuk.) Vannak tisztn jellsbeli problmk is: nem szabad, hogy a makro neve s az argumentumlistjt bevezet bal oldali zrjel kztt szkz legyen! Mindezek ellenre a makrk igen hasznosak. Erre nzve gyakorlati plda a 7. fejezetben ismertetend szabvnyos be- s kiviteli (I/O) knyvtr, amelyben a getchar s putchar fggvnyeket makrkknt definiljuk (putchar nyilvn argumentumot ignyel). Ezltal elkerljk azt a plusz terhelst, amit a minden egyes feldolgozand karakter esetben trtn fggvnyhvs jelentene. A makroprocesszor tovbbi szolgltatsait az A fggelk ismerteti. 4.9. Gyakorlat. Definiljuk a swap(x, y) makrt, amely megcserli a kt int tpus argumentumt! (A blokkstruktra segtsgnkre lesz.) _

105

5.

fejezet: Mutatk s tmbk

A mutat (pointer) olyan vltoz, amely egy msik vltoz cmt tartalmazza. A C-ben gyakran hasznlunk mutatkat, rszben azrt, mert nha csupn gy tudunk kifejezni valamilyen szmtst, rszben pedig azrt, mert hasznlatuk ltalban tmrebb s hatkonyabb kdot eredmnyez, mint amelyet ms mdon kaphatnnk. Azt szoktk mondani, hogy a mutat, csakgy, mint a goto utasts, csak arra j, hogy sszezavarja s rthetetlenn tegye a programot. Ez biztos gy is van, ha sz nlkl hasznljuk, hiszen knnyszerrel gyrthatunk olyan mutatkat, amelyek valamilyen nem vrt helyre mutatnak. Kell nfegyelemmel azonban a mutatkat gy is alkalmazhatjuk, hogy ezltal programunk vilgos s egyszer legyen. Ezt ksreljk meg bemutatni a kvetkezkben. 5.1. Mutatk s cmek Mivel a mutat valamilyen objektum cmt tartalmazza, rajta keresztl az illet objektum indirekt mdon rhet el. Tegyk fel, hogy x - pl. int - vltoz, px pedig mutat, amelyet ltre. Az & valamilyen eddig mg nem ismertetett mdon hoztunk

egyoperandus opertor valamely objektum cmt adja meg, teht a px = &x; utasts x cmt rendeli hozz a px vltozhoz; ilyenkor azt mondjuk, hogy px az x rtkre mutat (azt cmzi meg). Az & opertor csupn vltozkra s tmbelemekre alkalmazhat; az olyan konstrukcik, mint &(x + 1) vagy &3 nem megengedettek. Ugyancsak tilos valamely register vltoz cmre hivatkozni! A * egyoperandus opertor gy kezeli az operandust, mint a keresett rtk cmt, s megkeresi ezt a cmet, hogy tartalmt behozza. Ha teht y is int, akkor y = *px; annak a tartalmt rendeli y-hoz, amire px mutat. gy a px = &x; y = *px; szekvencia ugyanazt az rtket rendeli y-hoz, mint y = x; A mveletekben rszt vev vltozkat deklarlnunk is kell : int x, y; int *px; x s y deklarcija ugyanolyan, deklarcija azonban j.

mint

eddig.

px

mutat

int *px; azt fejezi ki, hogy a *px kombinci int tpus mennyisg, vagyis ha px a *px krnyezetben fordul el, akkor egy int tpus vltoznak felel meg. Gyakorlatilag a vltozk deklarcijnak szintaxisa azoknak a kifejezseknek a szintaxist utnozza, _melyekben az illet vltozk elfordulhatnak. Ez a meggondols mindig hasznos, mg bonyolult deklarcik esetben is. Pl. double atof (), *dp; azt fejezi ki, hogy kifejezsekben atof() s *dp double tpus rtkkel rendelkeznek. Vegyk szre tovbb, hogy a deklarci azt is kimondja, hogy a mutatnak mindig a megadott tpus objektumra kell mutatnia. Mutatk

106

kifejezsekben is elfordulhatnak. Ha pl. px az egsz tpus x-re mutat, akkor *px minden olyan szvegkrnyezetben elfordulhat, ahol x elfordulhat. y = *px + 1 hatsra y 1 -gyel nagyobb lesz, mint x; printf (%d \n, *px) kinyomtatja x pillanatnyi rtkt, tovbb d = sqrt((double)*px) d-ben ellltja x ngyzetgykt, ahol x double tpusv alakul t, mieltt taddna az sqrt fggvnynek (l. a 2. fejezetet). Az olyan kifejezsekben, mint y = *px + 1 a * s & egyoperandus opertorok szorosabban ktnek, mint az aritmetikai opertorok, gy a kifejezst kirtkel program elszr kiolvassa azt, amire px mutat, majd hozzad 1 -et, s hozzrendeli y-hoz. Rvidesen visszatrnk arra, hogy mit jelenthet y = *(px + 1) Mutathivatkozsok rtkadsok bal oldaln is elfordulhatnak. Ha px s x-re mutat, akkor *px = 0 az x-et kinullzza s *px += 1 vagy (*px)++ inkrementlja. Az utbbi pldban a zrjelek szksgesek: nlklk a kifejezs px-_t inkrementln, nem pedig azt, amire px mutat, mivel az egyoperandus opertorok, esetnkben * s ++ jobbrl balra rtkeldnek ki. Vgl, mivel a mutatk vltozk, ugyangy kezelhetk, mint a tbbi vltoz. Ha py egy msik, int tpus mennyisgre mutat, akkor py = px a px tartalmt py-ba msolja, miltal py ugyanarra fog mutatni, mint px. 5.2. Mutatk s fggvnyargumentumok Mivel a C nyelv az argumentumokat rtk szerinti hvs formjban adja t a fggvnyeknek, a hvott fggvny semmilyen kzvetlen mdon nem tudja megvltoztatni a hv fggvny vltozit. Mit tegynk, ha tnyleg meg kell vltoztatnunk valamelyik kznsges argumentumot? Pldnak okrt a rendezrutin megcserlhet kt, rossz sorrendben lev elemet a swap nev fggvny segtsgvel. Nem elg, ha azt rjuk, hogy swap (a, b); ahol a swap fggvny defincija:

107

swap (x, y) int x, y; { int temp; temp = x; x = y; y = temp; }

/*ROSSZ*/

Az rtk szerint trtn hvs miatt a swap nem kpes az t meghv rutinban elfordul a s b argumentumokat megvltoztatni. Szerencsre van lehetsg a kvnt hats elrsre. A hv rutin a megvltoztatand rtkeket megcmz mutatkat ad t: swap (&a, &b); Mivel az & opertor a vltoz cmt lltja el, &a az a vltozt megcmz mutat lesz. Magban a swap rutinban az argumentumokat mutatkknt deklarljuk, s az aktulis operandusokat ezeken keresztl rjk el: swap (px, py) { int temp; temp = *px; *px = *py; *py = temp; } A mutatargumentumokat gyakran alkalmazzk az olyan fggvnyekben, amelyeknek egynl tbb rtket kell visszaadniuk. (Mondhatjuk pl., hogy swap kt rtket ad vissza, tudniillik argumentumainak az j rtkeit.) Pldaknt tekintsk a getint fggvnyt, amely szabadformtum bemeneti konverzit vgez oly mdon, hogy egy karaktersorozatot egsz tpus rtkekre trdel, hvsonknt egy-egy egsz rtket szolgltatva. A getint fggvnynek vagy mr ltala tallt rtket kell visszaadnia, vagy pedig - amennyiben nincs tbb beolvasand karakter az llomny vge jelet. Az rtkeknek klnkln objektumokknt kell visszatrnik, fggetlenl attl, hogy milyen rtket hasznlunk EOF-knt, amely maga is egy beolvasott egsz mennyisg lehet. Az egyik megolds szerint, amely a 7. fejezetben ismertetsre kerl scanf beolvasfggvnyen alapul, getint az EOF-ot mint sajt fggvnyrtkt adja vissza olyankor, amikor megtallta az llomny vgt; minden ms visszatr rtk kznsges egsz szmra utal. A megtallt egsz szm numerikus rtke argumentumon keresztl addik vissza, amelynek egsz szmot megcmz mutatnak kell lennie. Ez a fajta szervezs elvlasztja az llomny vge llapotot a numerikus rtkektl. A kvetkez ciklus a getint hvsai segtsgvel egsz szmokkal tlt fel egy tmbt: int n, v, array [SIZE]; for (n = 0; n < SIZE && getint (&v) != EOF; n++) array [n] = v; Minden egyes hvs berja a v vltozba a bemeneten tallt kvetkez egsz szmot. Vegyk szre, hogy a getint argumentumaknt &y-t kell rnunk v helyett. Ha a puszta v-t hasznljuk, akkor valsznleg cmzsi hibt kvetnk el, mivel getint azt hiszi, hogy rvnyes mutatt kapott. /*px s py megcserlse*/ int *px, *py;

108

Maga a getint a mr korbban megrt atoi termszetes mdostsa: getint (pn) /*A kvetkez egsz beolvassa a bemenetrl*/ int *pn; { int c, sign; while ((c = getch ()) == || c == \n || c == \t) ; /*Az res kzt tugorja*/ sign = 1; if (c == + || c == -) {/*Feljegyzi az eljelet*/ sign = (c == +) ? 1 : -1; c = getch (); } for (*pn = 0; c >= 0 && c <= 9; c = getch ()) *pn = 10 * *pn + c - 0; *pn *= sign; if (c != EOF) ungetch ; return ; } A getint fggvnyben a *pn vgig kznsges int tipus vltozknt szerepel. Felhasznltuk a geth s ungeth fggvnyeket is (lersukat l. a 4. fejezetben), gy azt az egy plusz karaktert, amit mg ki kell olvasni, vissza lehet helyezni a bemenetre. 5.1. Gyakorlat. rjuk meg a getfloat fggvnyt, amely a getint lebegpontos megfelelje! Milyen tipust ad vissza getfloat fggvnyrtkknt? 5.3. Mutatk s tmbk A C nyelvben szoros kapcsolat van a mutatk s a tmbk kztt. Ez indokolja, hogy a mutatkkal s a tmbkkel egyidejleg foglalkozzunk. Valamennyi mvelet, amely tmbindexelssel vgrehajthat, mutatk hasznlatval ppgy elvgezhet. ltalban az utbbi vltozat gyorsabb, de klnsen a kezdk szmra els rnzsre nehezebben rthet. Az int a [10] deklarci definilja azt a tmbt, amelynek mrete 10, vagyis egy tz, egymst kvet objektumbl, az a[0], a[1], . . ., a[9] nev elemekbl ll blokkot hatroz meg. Az a[i] jellsmd a tmbnek a kezdettl szmtott i-edik pozcijt fejezi ki. Ha pa egszt megcmz mutat, amelyet int *pa deklarl, akkor a pa =&a[0] rtkads gy lltja be pa-t, hogy az az a nulladik elemre mutasson, vagyis pa az a[0] elem cmt tartalmazza. Ekkor az x = *pa rtkads a[0] tartalmt x-be msolja. Ha pa az a tmb adott elemre mutat, akkor definci szerint pa + 1 a tmb kvetkez elemre mutat. ltalban pa - i i elemmel pa el, pa + i pedig i elemmel pa mg mutat. gy ha pa

109

az a[0]-ra mutat, akkor *(pa + 1) a[1] tartalmt szolgltatja, pa+i az a[i] elem cme s *(pa+i) az a[i] elem tartalma. Ezek a megjegyzsek az a tmbben elhelyezked vltozk tipustol fggetlenl mindig igazak. Az adj 1-et a mutathoz s ennek kiterjesztseknt az egsz mutataritmetika alapdefincija, hogy a nvekmny mrtkegysge annak az objektumnak a trbeli mrete, amire a mutat mutat. gy pa+i esetben a pa-hoz hozzads eltt i azoknak az objektumoknak a mretvel szorzdik, amire pa mutat. Az indexels s a mutataritmetika kztt lthatan nagyon szoros kapcsolat van. Gyakorlatilag a tmbre val hivatkozst a fordt a tmb kezdett megcmz mutatv alaktja t. Ennek hatsra a tmb neve nem ms, mint egy mutatkifejezs, amibl szmos hasznos dolog kvetkezik. Mivel a tmb neve ugyanaz, mint az illet tmb nulladik elemnek cme, a pa = &a[0] rtkads gy is rhat, mint pa = a Legalbbis els rnzsre mg meglepbb az a tny, hogy az a[i]-re trtn hivatkozs *(a+i)-knt is rhat. a[i] kirtkelsekor a C fordt azonnal talaktja ezt *(a+i)-v; a kt alak teljesen egyenrtk. Ha az ekvivalencia mindkt elemre alkalmazzuk az & opertort, akkor azonnal kvetkezik, hogy &a[i] s a+i szintn azonosak: a+i az a-t kvet i-edik elem cme. Az rem msik oldala viszont az, hogy ha pa mutat, akkor azt kifejezsek indexelhetik: pa[i] azonos *(pa+i)-vel. Rviden, brmilyen tmb vagy indexkifejezs lerhat, mint egy mutat plusz egy eltols s viszont, akr egy utastson bell is. Van azonban egy fontos klnbsg a tmbnv s a mutat kztt, amire gyelnnk kell. A mutat vltoz, gy pa=a s pa++ rtelmes mveletek. A tmbnv azonban lland, nem pedig vltoz: az olyan konstrukcik, mint a=pa vagy a++, vagy p=&a nem megengedettek! Amikor a tmbnv egy fggvnynek addik t, a fggvny valjban a tmb kezdetnek cmt kapja meg. A hvott fggvnyen bell ez az argumentum vltoz, ugyangy, mint a tbbi, a tmbnvargumentum teht csakugyan mutat, vagyis egy cmet tartalmaz vltoz. E tnyt kihasznlva megrhatjuk az strlen karakterlnchossz-szmt fggvny j vltozatt: strlen (s) /*Visszaadja az s karakterlnc hosszt*/ char *s; { int n; for (n = 0; *s != \0; s++) n++; return (n); } s inkrementlsa teljesen megengedett, mivel a mutatk vltozk; s++-nak nincs hatsa az strlen hv fggvnybeli karakterlncra csupn a cmnek az strlen-ben tallhat msolatt inkrementlja. Fggvnydefinciban char s [];

110

s char *s; egyarnt szerepelhet formlis paramterknt; azt, hogy melyiket hasznljuk, nagymrtkben az dnti el, hogy miknt rjuk le a kifejezseket a fggvnyen bell. Amikor a tmbnv addik t valamelyik fggvnynek, a fggvny tetszse szerint hiheti azt, hogy tmbt vagy mutatt kapott, s ennek megfelelen kezelheti azt. Akr mindkt tpus mveletet hasznlhatja, ha ez clszernek s vilgosnak ltszik. Lehetsg van arra, hogy a tmbnek csupn egy rszt adjuk t valamelyik fggvnynek oly mdon, hogy a rsztmb kezdett megcmz mutatt adunk t. Ha pl. a egy tmb neve, akkor f (&a[2]) s f (a+2) egyarnt az a[2] elem cmt adja t az f fggvnynek, mivel &a[2] s a+2 egyarnt mutatkifejezs, mindkett az a tmb harmadik elemre vonatkozik. f-en bell az argumentumdeklarci akr f (arr) int arr []; { . . . } akr f (arr) int *arr; { . . . } is lehet. Ami f-et illeti, az a tny, hogy az argumentum valjban egy nagyobb tmb egy rszre vonatkozik, semmifle kvetkezmnnyel sem jr. 5.4. Cmaritmetika Ha p mutat, akkor p++ oly mdon inkrementlja p-t, hogy az a megcmzett tetszleges tpus objektum kvetkez elemre, p += i pedig gy, hogy az a pillanatnyilag megcmzett elem utni i-edik elemre mutasson. Az ilyen s hasonl szerkezetek a mutat- vagy cmaritmetika legegyszerbb s legkznsgesebb formi. A C nyelv kvetkezetes s szablyos mdon kzelt a cmaritmetikhoz; a mutatk, tmbk s a cmaritmetika egysges kezelse a nyelv egyik legfbb ernye. Szemlltessk ezt azzal, hogy megrunk egy elemi trfoglal programot (amely azonban egyszersge ellenre is hasznlhat)! Kt rutinunk van: az alloc(n) rutin n egymst kvet karakterpozcit megcmz p mutatt ad vissza, amelyet az alloc hvja karakterek trolsra hasznlhat; tovbb free(p), amely felszabadtja az alloc rutinnal nyert trterletet ksbbi hasznlat cljra. A rutinok elemiek, mivel free hvsai fordtott sorrendben kell, hogy trtnjenek, mint az alloc hvsai. gy az alloc s a free ltal kezelt trterlet egy verem, vagyis egy utols-be, els-ki (last-in, first-out) lista. A szabvnyos C knyvtrban rendelkezsre llnak az ezeknek megfelel fggvnyek, amelyekre azonban nem vonatkoznak ilyen megszortsok, s a 8. fejezetben is bemutatunk javtott vltozatokat. Addig azonban szmos alkalmazshoz megfelel a trivilis alloc is, ha arra van szksgnk, hogy elre nem lthat mret kisebb

111

trterletek elre nem lthat idpontokban rendelkezsre lljanak. A legegyszerbb megvalstsban az alloc egy allocbufnak nevezett nagy karaktertmb darabjait szolgltatja. Ez a tmb az alloc s a free kizrlagos tulajdona. Mivel ez a kt rutin mutatkat s nem tmbindexeket hasznl, a tmb nevt egyetlen ms rutinnak sem kell ismernie, gy az static extern-knt deklarlhat, vagyis csak az alloc s a free fggvnyeket tartalmaz llomnyban lesz rvnyes s azon kvl lthatatlan. A gyakorlatban akr nem is kell, hogy nvvel rendelkezzen a tmb: ehelyett gy is elllthat, hogy a program az opercis rendszertl elkr valamilyen nv nlkli trblokkot megcmz mutatt. Tudnunk kell azt is, hogy mennyi kerlt felhasznlsra allocbufbl. E clbl egy, a kvetkez szabad elemet megcmz mutatt hasznlunk, amelynek neve allocp. Ha valaki az alloc-tl n karaktert kr, akkor az ellenrzi, hogy maradt-e mg ennyi hely allocbuf-ban. Ha igen, akkor alloc visszaadja az allocp pillanatnyi rtkt (vagyis a szabad blokk kezdcmt), majd azt n-nel inkrementlja, hogy a kvetkez szabad terletre mutasson. free(p) egyszeren p-re lltja be allo_p-t, ha p az allocbuf-on bell van. #define NULL 0 /*Mutat a hibajelzshez*/ #define ALLOCSIZE 1000 /*A rendelkezsre ll terlet mrete*/ static char allocbuf [ALLOCSIZE];/*Trhely alloc-nak*/ static char *allocp = allocbuf; char *alloc (n) int n; /*Kv. szabad hely*/

/*n karaktert megcmz mutatt ad vissza*/

{ if (allocp + n <= allocbuf + ALLOCSIZE) { return (allocp - n); /*Rgi p*/ } else /*Nincs elg hely*/ return (NULL); }

/*Befr*/ allocp += n;

free (p) /*p ltal megcmzett terlet felszabadtsa*/ char *p; { if (p >= allocbuf && p < allocbuf + ALLOCSIZE) allocp = p; } Nhny megjegyzs: ltalban a mutat ppen gy inicializlhat, mint brmilyen ms vltoz, noha kznsges esetben rtelmes rtk csupn a NULL (l. a tovbbiakban) vagy olyan kifejezs lehet, amely a korbban definilt megfelel tpus adatok cmeit tartalmazza. A static char *allocp = allocbuf; deklarci gy definilja allocp-t, hogy az karaktermutat legyen, s gy inicializlja, hogy allocbuf-ra mutasson, amely a kvetkez szabad pozci a program indtsakor. Ezt gy is rhattuk volna, hogy:

112

static char *allocp = &allocbuf [0]; mivel a tmb neve egyben a nulladik elemnek a cme; mindig a termszetesebb vltozatot hasznljuk! Az if (allocp + n <= allocbuf + ALLOCSIZE) vizsglat ellenrzi, hogy van-e elegend hely az n szm karakter elhelyezsre vonatkoz krs teljestsre. Ha igen, akkor az allocp j rtke legfeljebb eggyel mutat tl az allocbuf vgn. Ha a krs kielgthet, az alloc kznsges mutatval tr vissza (figyeljk meg magnak a fggvnynek a deklarcijt). Ha a krs nem teljesthet, akkor az alloc-nak valamilyen jel visszaadsval kell jeleznie, hogy nem maradt hely. A C nyelv gondoskodik arrl, hogy semmifle olyan mutat, amely rvnyes mdon adatra mutat, nem tartalmazhat nullt, gy a nulla visszatrsi rtk hasznlhat az abnormlis esemny jelzsre, adott esetben annak kzlsre, hogy nincs hely. Szmszer nulla helyett azonban NULL-t runk, hogy ezzel vilgosabban jelezzk : ez a mutat klnleges rtke. ltalban egsz szmok nem rendelhetk rtelmes mdon mutatkhoz, a nulla specilis eset. Az olyan vizsglatok, mint if (alloc + n <= allocbuf + ALLOCSIZE) s if (p >= allocbuf && p < allocbuf + ALLOCSIZE) a mutataritmetika lnyeges sajtossgaira vilgtanak r. Elszr is, a mutatk bizonyos krlmnyek kztt sszehasonlthatk. Ha p s q ugyanannak a tmbnek az elemeire mutat, akkor az olyan relcik, mint <, >= stb. megfelelen mkdnek. P < q pl. akkor igaz, ha p a tmb kisebb sorszm elemre mutat, mint q. Az == s != relcik ugyancsak alkalmazhatk. Brmilyen mutat nullval val egyenlsge vagy nemegyenlsge rtelmes mdon ellenrizhet. Vigyzat! Ne vgezznk viszont klnbz tmbket megcmz mutatkkal aritmetikai mveleteket vagy sszehasonltsokat! Ha szerencsnk van, akkor minden gpen nyilvnval rtelmetlensget kapunk. Ha azonban nincs szerencsnk, akkor a programunk mkdni fog az egyik gpen, de rejtlyes mdon ssze fog omlani egy msikon. Msodszor lttuk, hogy az egsz szmot megcmz mutatkkal sszeads s kivons vgezhet. p + n a p ltal ppen megcmzett objektumot kvet n-edik objektumot jelenti. Ez igaz, fggetlenl attl, hogy a p-t milyen tpus objektumot megcmz mutatnak deklarltuk: a fordt n-et olyan egysgekben szmllja, amelyek megfelelnek a p ltal megcmzett objektum mretnek, amely utbbit p deklarcija hatrozza meg. A PDP-11-en pldul a mretfaktor char esetben 1 short-nl 2, long s float esetn 4 s double esetn 8. Mutatk kivonsa szintn megengedett: ha p s q ugyanannak a tmbnek az elemeire mutatnak, akkor p - q a p s q kztti elemek darabszma. E tnyt kihasznlva megrhatjuk az strlen jabb vltozatt: strlen (s) /*Visszaadja az s karakterlnc hosszt*/ char *s; { char *p = s; while (*p != \0) p++; return (p - s); }

113

A deklarciban p kezdeti rtkeknt s-et adtuk meg, vagyis p kezdetben az s els karakterre mutat. A while ciklusban addig vizsgljuk az egymst kvet karaktereket, amg a vget jelz \0 el nem kerl. Mivel \0 rtke nulla, s mivel a while csupn azt vizsglja, hogy a kifejezs nulla-e, elhagyhat az explicit vizsglat. Az ilyen ciklusokat gyakran az albbi alakban rjk: while (*p) p++; Minthogy p karakterekre mutat, p++ minden alkalommal a kvetkez karakterre lpteti p-t, s p - s az tlpett karakterek szmt, vagyis a karakterlnc hosszt adja meg. A mutataritmetika kvetkezetes: ha float mennyisgekkel dolgoznnk, amelyek a charoknl tbb trterletet foglalnak el, s ha p float-ot megcmz mutat lenne, akkor p++ a kvetkez float-ra lptetne. gy az alloc msik vltozatt, amely pl. char-ok helyett float vltozkkal dolgozik, egyszeren gy rhatjuk meg, hogy az alloc-ban s free-ben vgig float-okra cserljk a char vltozkat. Az sszes mutatmvelet automatikusan szmtsba veszi a megcmzett objektum mrett, gy semmi egyebet nem kell megvltoztatni. Az emltett mveleteken kvl (mutat s integer sszeadsa s kivonsa, kt mutat kivonsa s sszehasonltsa) minden ms mutatmvelet tilos! Nincs megengedve kt mutat sszeadsa, szorzsa, osztsa, mutatk lptetse, maszkolsa, sem pedig float vagy double mennyisgeknek mutatkhoz trtn hozzadsa. 5.5. Az Karaktermutatk s fggvnyek

Ez itt egy karakterlnc alak karakterlnclland nem ms, mint egy karaktertmb. A fordt a bels brzolsban a tmbt a \0 karakterrel zrja le, hogy a programok megtallhassk a karakterlnc vgt. A trterlet hossza teht eggyel nagyobb, mint az idzjelek kztti karakterek szma. A karakterlnc-lland leggyakrabban taln fggvnyargumentumokban fordul el, mint pl. printf (Figyelem, emberek\n); A programban ily mdon megjelen karakterlnc karaktermutatn keresztl rhet el; printf a karaktertmbt megcmz mutatt kap. A karaktertmbknek termszetesen nem kell felttlenl deklarlja, akkor a message = now is the time; /*Ideje*/ utasts message-hez tnyleges karaktereket megcmz mutatt rendel hozz. Ez karakterlnc-msols, a dolog csak a mutatkat rinti. a nem

A C nyelvben nincsen olyan opertor, amellyel teljes karakterlncot egy egysgknt dolgozhatnnk fel. A mutatk s tmbk tovbbi vonatkozsait a 7. fejezetben ismertetend szabvnyos bes kiviteli (I/O) knyvtr kt hasznos fggvnyn keresztl mutatjuk be. Az els fggvny az strcpy(s, t), amely a t karakterlncot az s karakterlncba msolja. Az argumentumokat az rtkadshoz val hasonlsg miatt rtuk ebben a sorrendben, hiszen a t karakterlncnak az s karakterlnchoz trtn hozzrendelsekor azt mondannk, hogy s = t

114

Elszr a tmbs vltozatot mutatjuk be: strcpy (s, t) /*t msolsa s-be*/ char s [], t []; { int i; i = 0; while ((s [i] =t [i]) != \0) i++; } sszehasonltsul me az strcpy mutatval rt vltozata: strcpy (s, t) /*t msolsa s-be; 1. mutatt alkalmaz vltozat*/ char *s, *t; { while ((*s = *t) != \0) { s++; t++; } } Mivel az argumentumok tadsa rtk szerint trtnik, strcpy tetszs szerinti mdon hasznlhatja s-t s t-t. Az adott esetben ezek alkalmas mdon inicializlt mutatk, amelyek karakterenknt vgighaladnak a tmbkn, amg a t-t lezr \0 t nem msoldik s-be. A gyakorlatban strcpy-t nem az elbb bemutatott mdon rnnk meg. Egy msik lehetsg pl. : strcpy (s, t) /*t msolsa s-be; 2. mutatt alkalmaz vltozat*/ char *s, *t; { while ((*s++ = *t++) != \0) ; } Ez a programkd s s t inkrementlst a felttelvizsglatba helyezi t. *t++ rtke az a karakter, amire t inkrementls eltt mutatott; a ++ postfixum mindaddig nem vltoztatja meg t-t, amg ez a karakter feldolgozsra nem kerlt. Hasonlkppen, s inkrementlsa eltt a karakter a rgi s pozciban troldik. Egyben ez a karakter lesz az az rtk, amelyet a ciklusvezrls rdekben \0-val sszehasonltunk. Vgeredmnyben a karakterek a zr \0-ig, a zr \ 0-t is belertve tmsoldnak t-bl s-be. Vgs lervidtsknt vegyk ismt szre, hogy a \0-val val sszehasonlts redundns, ezrt a fggvny gyakran gy jelenik meg: strcpy (s, t) /*t msolsa s-be; 3. mutatt alkalmaz vltozat*/ char *s, *t; { while (*s++ = *t++) ; } Br els rnzsre titokzatosnak tnhet, ez a jells nagyon knyelmes, s mr csak azrt is el kell sajttanunk, mert gyakran tallkozunk vele C programokban.

115

A msik rutin az strcmp(s, t), amely sszehasonltja az s s t karakterlncokat, s negatv szmot, nullt vagy pozitv szmot ad vissza aszerint, hogy s lexikografikusan kisebb, mint t, egyenl tvel vagy nagyobb, mint t. A visszaadott rtket gy nyerjk, hogy az els olyan pozcin, ahol s s t nem egyeznek meg, kivonjuk egymsbl a karaktereket. strcmp (s, t) char s [], t []; { int i; i = 0; while (s [i] == t [i]) if (s [i++] == \0) return (0); return (s [i] - t [i]); } Az strcmp mutat alkalmazsval: strcmp (s, t) char *s, *t; { for (; *s == *t; s++,t++) if (*s == \0) return (0); return (*s - *t); } Mivel ++ s -- akr prefix, akr postfix opertorok lehetnek, ritkbban ugyan, de a * s ++, ill.ms kombincii is elfordulhatnak. Pl. *++p p-t mg azeltt inkrementlja, hogy karakterhez val hozzfrs megtrtnne. *--p elszr dekrementlja p-t. 5.2. Gyakorlat. rjuk t a 2. fejezetben bemutatott strcat fggvnyt mutat alkalmazsval (strcat(s, t) a t karakterlncot az s karakterlnc vgre msolja)! 5.3. Gyakorlat. rjunk makrt strcpy-ra! 5.4. Gyakorlat. rjuk t a korbbi fejezetek erre alkalmas programjait s gyakorlatait gy, hogy tmbindexels helyett mutatkat hasznlunk! J lehetsg pl. a getline (l. az 1. s 4. fejezetet), az atoi az itoa s vltozataik (l. a 2., 3., 4. fejezetet), valamint az index s a getop (4. fejezet). 5.6. A mutatk nem egsz szmok Rgebbi C programok igen liberlisan kezeltk a mutatk msolsnak krdst. ltalban a legtbb gpen a mutatt hozz lehetett rendelni egy egsz tpus_ mennyisghez s viszont, anlkl, hogy maga a mutat megvltozott volna sem mretszmts, sem konverzi nem trtnt, nem vesztek el bitek. Sajnos azonban ez oda vezetett, hogy sokan szabadosan kezeltk a mutatkat visszaad rutinokat, s a kapott mutatkat egyszeren ms rutinoknak adtk t gyakran elmulasztva a szksges mutatdeklarcikat. Tekintsk pl. az strsave(s) fggvnyt, amely az alloc hvsval kapott biztos helyre msolja az s a p ltal megcmzett /*A visszatr rtk < 0, ha s < t; 0, ha s == t; > 0, ha s > t*/ /*A visszatr rtk < 0, ha s < t; 0, ha s == t, > 0, ha s > t*/

116

karakterlncot, majd az azt program helyesen gy fest : char *strsave (s)

megcmz

mutatval

tr

vissza.

/*Elmenti az s karakterlncot*/ char *s;

{ char *p, *alloc (); if ((p = alloc (strlen (s) + 1)) != NULL) strcpy (p, s); return (p); } A gyakorlatban ers a ksrts, hogy elhagyjuk a deklarcikat: strsave (s) /*Elmenti az s karakterlncot*/ { char *p; if ((p = alloc (strlen (s) + 1)) != NULL) strcpy (p, s); return (p); } Ez sok gpen mkdni fog, mivel a fggvnyek s argumentumok alaprtelmezs szerinti tpusa int, s az int-ek s mutatk kztt ltalban mindkt irnyban biztonsgosan vgezhet hozzrendels. Mgis, az ilyenfajta programkd hasznlata kockzatos, mivel hatsa az adott nyelvi megvalststl s a gpi architektrtl fgg, s gy az ltalunk ppen hasznlt fordt esetben esetleg nem a vrt mdon mkdik. sszerbb teht, ha minden egyes deklarcit kirunk. (Ha errl mgis elfeledkeznnk, a lint program figyelmeztet az ilyen esetekre.) 5.7. Tbbdimenzis tmbk A C nyelv mtrixjelleg tbbdimenzis tmbk hasznlatt is megengedi, noha a gyakorlatban ilyeneket sokkal ritkbban hasznlunk, mint mutattmbket. Ebben a szakaszban ezek nhny tulajdonsgt mutatjuk be. Tekintsk a dtumkonverzi problmjt: a hnap adott napjnak az v egy napjv s vissza trtn alaktst. Pldnak okrt a mrcius 1. nem szkvekben a 60., szkvekben a 61. nap. Az talaktsok elvgzsre definiljunk kt fggvnyt: day_of_year a hnapot s napot az v napjv, mg month_day az v adott napjt hnapp s napp alaktja t. Mivel az utbbi fggvny kt rtkkel tr vissza, a hnap s a nap argumentum mutat lesz: month_day (1977, 60, &m, &d) hatsra m 3 s d 1 lesz (mrcius 1 .). Mindkt fggvnynek ugyanarra az informcira van szksge, tudniillik az egyes hnapok napjainak szmt tartalmaz tblzatra. Mivel a hnapokban lev napok szma eltr a szkvekben s a nem szkvekben, egyszerbb, ha ezeket egy ktdimenzis tmb kt sorban elklntjk, mint ha a szmts sorn prblnnk nyomonkvetni, hogy mi is trtnik februrban. A tmb s az talaktsokat vgz fggvnyek a kvetkezk : static int day_tab [2][13] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31} }; day_of_year (year, month, day) /*Az ven belli napsorszm kiszmtsa

117

a hnapbl s napbl*/ int year, month, day; { int i, leap; leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0; for (i = 1; i < month; i++) day += day_tab [leap][i]; return (day); } month_day (year, yerday, pmonth, pday) /*Hnap s nap kiszmtsa az*/ int year, yearday, *pmonth, *pday; /*v adott sorszm napjbl*/ { int i, leap; leap = year % 4 == 0 && year % 100 != 0 || year % 400 == 0; for (i = 1; yearday > day_tab [leap][i]; i++) yearday -= day_tab [leap][i]; *pmonth = i; *pday = yearday; } A day_tab tmbnek kvl kell lennie mind a day_of_year, mind pedig a month_day fggvnyen, hogy mindketten hasznlhassk. A day_tab az els ktdimenzis tmb, amellyel eddig tallkoztunk. A C-ben definci szerint a ktdimenzis tmb valjban olyan egydimenzis tmb, amelynek minden eleme tmb. Ezrt rjuk az indexeket day_tab [i][j] nem pedig day_tab [i,j] alakban, mint a legtbb ms nyelvben. Ettl eltekintve a ktdimenzis tmb ugyangy kezelhet, mint a tbbi nyelvben. Az elemek soronknt troldnak, vagyis a legjobboldalibb index vltozik a leggyorsabban, amikor az elemekhez a trols sorrendjben trtnik hozzfrs. A tmbt a kezdeti rtkek kapcsos zrjelek kz zrt listjval inicializljuk; a ktdimenzis tmb minden sort a megfelel allista inicializlja. A day_tab tmbt egy nullkat tartalmaz oszloppal kezdtk, hogy a hnapszmok ne 0-tl 11 ig, hanem a megszokott mdon 1 -tl 12-ig fussanak. Mivel az adott esetben az elfoglalt trhely mennyisge nem lnyeges, ez a megolds egyszerbb, mint az indexek kiigaztsa. Ha a ktdimenzis tmbt fggvnynek kell tadni, a fggvnybeli argumentumdeklarcinak tartalmaznia kell az oszlopmretet; a sormret kzmbs, mivel mint korbban, most is mutatt adunk t. Az adott esetben ez a 13 int-et tartalmaz tmbkre mint objektumokra mutat. gy, ha a day_tab tmbt kell tadni az f fggvnynek, akkor f deklarcija: f (day_tab) int day_tab [2][13]; {

118

. . . } Az f-beli argumentumdeklarci lehetne int day_tab [][13]; is, mivel a sorok szma kzmbs, vagy lehetne int (*day_tab)[13]; amely azt fejezi ki, hogy az argumentum egy 13 egszbl ll tmbt jell mutat. A zrjelek szksgesek, mivel [ ] (szgletes zrjelek) precedencija nagyobb, mint a * szimblum, gy zrjelek nlkl az int *day_tab [13]; deklarci egy 13 darab, egszt megcmz mutatbl ll tmbt jelent, amint ezt a kvetkezkben ltni fogjuk. 5.8. Mutattmbk; mutatkat megcmz mutatk Mivel a mutatk maguk is vltozk, joggal vrhat, hogy mutatkbl ll tmbk is hasznlhatk. Ez valban gy van. E lehetsget olyan program megrsn keresztl mutatjuk be, amely egy szvegsorokbl ll halmazt alfabetikus sorrendbe rendez: ez a UNIX-beli sort rendez segdprogram egyszerstett vltozata. A 3. fejezetben bemutattuk a Shell sort fggvnyt, amely egszekbl ll tmbt rendez. Ugyanez az algoritmus fog itt is mkdni, attl eltekintve, hogy most szvegsorokkal kell foglalkoznunk, amelyek klnbz hosszsgak, s az egszektl eltren nem hasonlthatk ssze, s egyetlen mvelettel nem mozgathatk. Olyan adatbrzolsra van szksgnk, amely hatkonyan s knyelmesen brkzik meg vltoz hosszsg szvegsorokkal. Itt lpnek be a mutatkbl ll tmbk. Ha a rendezend sorokat elejtl vgig egyetlen hossz karaktertmbben troljuk (amelyet esetleg az alloc kezel), akkor minden sor elrhet az els karaktert megcmz mutatn keresztl. Maguk a mutatk egy tmbben trolhatk. Kt sor oly mdon hasonlthat ssze, hogy mutatikat tadjuk strcmp-nek. Ha kt, nem megfelel sorrendben lev sort meg kell cserlni, akkor a mutattmbben lev mutatk cserldnek fel, nem pedig maguk a szvegsorok. Ezltal elkerljk azt a kt sszetartoz problmt, amit a bonyolult trkezels s a tnyleges szvegsorok mozgatsval egyttjr nagy megterhels jelentene. A rendezs folyamata hrom lpsbl tevdik ssze: az sszes bemeneti sor beolvassa, a beolvasott sorok rendezse, a helyes sorrendben trtn kinyomtats. Szoks szerint legclszerbb, ha a programot olyan fggvnyekre bontjuk fel, amelyek ezt a termszetes felosztst kvetik, s a frutin vgzi a folyamat vezrlst. Egy pillanatra hagyjuk a rendezsi lpst s sszpontostsunk az adatstruktrra, valamint a be- s kivitelre. A bemeneti rutinnak ssze kell gyjtenie, majd trolnia kell az egyes sorok karaktereit s ssze kell lltania a sorokat megcmz mutatk tmbjt. Ugyancsak a bemeneti rutinnak kell megszmllnia a berkez sorokat, mivel erre az informcira a rendezskor s nyomtatskor szksg lesz. Tekintve, hogy a beolvasfggvny csak vges szm bemeneti sorral tud megbrkzni, valamifle nemltez sor-darabszmot, pl. -1-et ad vissza, ha tl sok szveg rkezett. A kimeneti rutinnak abban a sorrendben kell kinyomtatnia a sorokat, amelyben azok a mutatk tmbjben megjelennek.

119

#define NULL 0 #define LINES 100 /*A rendezend sorok max. szma*/ main () /*Beolvasott sorok rendezse*/ { char * lineptr [LINES]; /*A szvegsorokatmegcmz mutatk*/ int nlines; /*A beolvasott sorok szma*/ if ((nlines = readlines (lineptr, LINES)) >= 0) { sort (lineptr, nlines); writelines (lineptr, nlines); } else printf (a bemenet tl nagy a rendezshez \n); } #define MAXLEN 1000 readlines (lineptr, maxlines) /*Sorok beolvassa*/ char *lineptr []; /*Rendezshez*/ int maxlines; { int len, nlines; char *p, *alloc (), line [MAXLEN]; nlines = 0; while ((len = getline (line, MAXLEN)) > 0) if (nlines >= maxlines) return (-1); else if ((p = alloc (len)) == NULL) return(-1); else { line [len - 1] = \0; lineptr [nlines++] = p; } return (nlines); } A sorok vgn tallhat jsor karakterek befolysoljk a rendezsi sorrendet. writelines (lineptr, nlines) { int i; for (i = 0; i < nlines; i++) printf (%s \n, lineptr [i]); } A legfontosabb j dolog lineptr deklarcija: char *lineptr [LINES]; azt jelenti, hogy a lineptr egy LINES szm elembl ll, mutatkat tartalmaz tmb, amelynek minden eleme egyegy char-ra mutat. Ms szval lineptr [i] karaktermutat, s *lineptr [i] egy karakterhez fr hozz. Mivel lineptr tmb, amelyet writelines-nak adunk t, pontosan ugyangy kezelhetjk mutatknt, mint azt a korbbi pldkban lttuk. A fggvny teht gy is rhat: writelines (lineptr, nlines) /*A kimenetre kerl sorok krsa*/ char *lineptr []; int nlines; sorok kirsa*/ char *lineptr []; int nlines; trldnek, gy nem /*Az jsort levgja*/ strcpy (p, line);

/*A kimenetre kerl

120

{ while (--nlines >= 0) printf (%s \n, *lineptr++); } A *lineptr kezdetben az els sorra mutat; minden inkrementls a kvetkez sorra lpteti, mikzben nlines-t leszmlljuk. Most, hogy a be- s kimenet a keznkben van, rtrhetnk a rendezsre. A 3. fejezetben ltott Shell rendezprogramot kismrtkben meg kell vltoztatnunk: mdostani kell a deklarcikat, s az sszehasonlts mvelett kln fggvnyben kell elhelyezni. Az alapvet algoritmus vltozatlan, ezrt bzhatunk abban, hogy a program tovbbra is mkdni fog. sort (v, n) /*A v [0] ... v [n - 1] karakterlncok rendezse nvekv sorrendben*/ char *v []; int n; { int gap, i, j; char *temp; for (gap = n/2; gap > 0; gap /= 2) for (i = gap; i < n; i++) for (j = i - gap; j >= 0; j -= gap) { if (strcmp (v [j], v [j + gap]) <=0) break; temp = v [j]; v [j] = v [j + gap]; v [j + gap] = temp; } } Mivel v (azaz lineptr) minden egyes eleme karaktermutat, temp-nek is annak kell lennie, hogy az egyik a msikba msolhat legyen. A programot a lehet legegyszerbbre rtuk meg, hogy minl gyorsabban el tudjuk indtani. Lehetne azonban gyorsabb is, ha pl. a bejv sorokat kzvetlenl a readlines ltal karbantartott tmbbe msolnnk, nem pedig elszr a line-ba, majd az alloc ltal kezelt, rejtett helyre. Az els vltozatot azonban blcsebb gy megrni, hogy minl rthetbb legyen. A hatkonysggal rrnk ksbb foglalkozni. Programunkon valsznleg nem sokat gyorstana, ha kikszblnnk a bemeneti sorok szksgtelen tmsolst. Lnyeges javulst csak az hozhat, ha a Shell sort programjt valami jobbal, pl. a quicksort programmal cserljk fel. Az 1. fejezetben rmutattunk, hogy mivel a while s a for ciklusok a vgfelttelt a ciklustrzs els vgrehajtsa eltt vizsgljk, hozzjrulnak ahhoz, hogy a programok a lehet leggyorsabban mkdjenek, klnsen, ha nincs bemenet. Tanulsgos, ha vgigmegynk a rendezprogram fggvnyein, s megvizsgljuk, mi trtnik, ha egyltaln nincs bemeneti szveg. 5.5. Gyakorlat. rjuk jra a readlines fggvnyt oly mdon, hogy a sorokat a main ltal adott tmbben hozzuk ltre s nem az alloc-ot hvjuk a tr karbantartsa cljbl! Mennyivel gyorsabb gy a program? 5.9. Mutattmbk inicializlsa rjuk meg a month_name(n) fggvnyt, amely egy olyan mutatt ad vissza, amely az n-edik hnap nevt tartalmaz karakterlncra mutat. Ez a bels static tmb idelis alkalmazsa. A month_name a karakterlncok szmra kln tmbt tartalmaz, s hvs utn a megfelel karakterlncot megcmz mutatt adja vissza. E szakasz tmja az, hogy hogyan kell a nevek tmbjt inicializlni.

121

A szintaxis hasonlt a korbbi inicializlsokra: char *month_name (n) /*Visszaadja az n-edik hnap nevt*/ int n; { static char *name [] = { nem ltez hnap, janur, februr, mrcius, prilis, mjus, jnius, jlius, augusztus, szeptember, oktber, november, december } return ((n < 1 || n > 12) ? name [0] : name [n]); } A name karaktermutatkbl ll tmb. Deklarcija ugyanaz, mint lineptr- a rendezsi pldban. A kezdeti rtk egyszeren a karakterlncok listja; mindegyik hozz van rendelve a tmb megfelel pozcijhoz. Pontosabban szlva az i-edik karakterlnc karakterei valamilyen ms helyre kerlnek, s az ket megcmz mutat name [i]ben tallhat. Mivel a tmb mrete nincs megadva, maga a fordt szmllja meg a kezdeti rtkeket s tlti be a helyes darabszmot. 5.10. Mutatk s tbbdimenzis tmbk A kezd C programozkat gyakran megzavarja a ktdimenzis tmbk s az olyan mutattmbk kztti klnbsg, mint amilyen a fenti pldban name volt. Pl. az int a [10][10]; int *b [10]; deklarcikban a s b hasznlata ugyan hasonl, amennyiben a[5] [5] s b[5] [5] egyarnt egy bizonyos int-re trtn megengedett hivatkozsok. a azonban igazi tmb : 100 trrekeszt foglaltunk le szmra, egy adott elemt a hagyomnyos mtrixszer indexszmtssal vlaszthatjuk ki. b esetben azonban a deklarci csupn 10 mutatt foglal le: ezek mindegyikt gy kell belltani, hogy egy-egy egszekbl ll tmbre mutasson. Feltve, hogy mindegyikk egy-egy tzelem tmbre mutat, 100 trrekeszt foglaltunk le, ezenkvl tovbbi tz rekeszre van szksg a mutatk szmra. gy a mutatkbl ll tmb valamivel tbb terletet foglal el, s explicit inicializlst ignyelhet. Van azonban kt elnye: egyrszt az elemeket nem szorzssal s sszeadssal, hanem mutatn keresztl, indirekt mdon rjk el, msrszt a tmb sorai klnbz hosszsgak lehetnek. gy nem szksges, hogy b minden eleme egy-egy tzelem vektorra mutasson: lehet kztk amelyik kt elemre, esetleg hszra, st amelyik egyetlen elemre sem mutat. Br az elbbi fejtegetsben egszekrl beszltnk, a mutattmbk hasznlatnak messze leggyakoribb esete az, amelyet a month_name-ben mutattunk be: klnbz hosszsg karakterlncok kezelse. 5.6. Gyakorlat. rjuk t a day_of_year s a month_day rutint oly mdon, hogy indexels helyett mutatkat hasznljunk!

122

5.11. Parancssor-argumentumok A C nyelvet tmogat krnyezetekben lehetsg van arra, hogy a vgrehajts megkezdsekor a programnak parancssor-argumentumokat vagy paramtereket adjunk t. Amikor a vgrehajts megkezdsekor main-t meghvjuk, a hvsban kt argumentum szerepel. Az els (amit szoks szerint argc-nek neveznk) azoknak a parancssorargumentumoknak a darabszma, amelyekkel a programot meghvtuk. A msodik argumentum (argv) egy mutat: ez arra a karakterlnctmbre mutat, amely az elbbi argumentumokat tartalmazza. Egy karakterlnc egy argumentumnak felel meg. E karakterlncok kezelse a tbbszrs mlysg mutathasznlat tipikus esete. A tbbszint mutathasznlathoz szksges deklarciknak, a mdszer alkalmazsnak legegyszerbb szemlltet pldja az echo program, amely egyszeren megismtli (visszhangozza) az egy sorban megjelen, szkzkkel elvlasztott parancssor-argumentumokat. Vagyis, ha kiadjuk az echo Figyelem, emberek parancsot, akkor a kimeneten Figyelem, emberek jelenik meg. Megllapods szerint argv [0] az a nv, amellyel a programot hvtk, gy argc legalbb 1 . Az elbbi pldban argc 3 s argv[0], argv [1], ill. argv [2] sorra echo, Figyelem s emberek. Az els igazi argumentum argv [1] s az utols argv[n-1]. Ha argc rtke 1, akkor a program nevt nem kvetik parancssor-argumentumok. Mindezt az echo programban mutatjuk be: main (argc, argv) /*Visszhangozza az argumentumokat 1. vltozat*/ int argc; char *argv []; { int i; for (i = 1; i < argc; i++) printf (%s %c, argv [i], (i < argc-1) ? : \n); } Mivel argv mutattmbt megcmz mutat, tbbflekppen is megrhatjuk ezt a programot gy, hogy a tmb indexelse helyett a mutatt kezeljk. Lssunk kt vltozatot : main (argc, argv) /*Visszhangozza az argumentumokat ; 2. vltozat*/ int argc; char *argv []; { while (--argc > 0) printf (%s %c, *++argv,(argc > 1) ? : \n); } Mivel argv az argumentum-karakterlncok tmbjnek kezdett megcmz mutat, 1-gyel trtn inkrementlsnak (++argv) hatsra argv[0] helyett az eredeti argv[1]-re fog mutatni. Az egymst kvet inkrementlsok hatsra mindig a kvetkez argumentumra lp; *argv az illet argumentumot megcmz mutat. Ezzel egyidejleg argc dekrementldik: amikor nullv vlik, nincs tbb kinyomtatand argumentum. Egy msik vltozat : main (argc, argv) /*Visszhangozza az argumentumokat

123

3. vltozat*/ int argc; char *argv []; { while (--argc > 0) printf ((argc > 1) ? %s : %s \n, *++argv); } Ez a vltozat azt mutatja be, hogy a printf formtumargumentuma ppgy lehet kifejezs, mint brmilyen ms fggvny. Ez a fajta hasznlat nem tl gyakori, de rdemes r emlkezni. Msodik pldaknt vgezznk nhny javtst a 4. fejezet mintakeres programjban. Taln emlkeznk arra, hogy a keressi minta mlyen a programon bell helyezkedett el, ami nem valami kellemes megolds. Az UNIX grep segdprogramjnak fonalt kvetve vltoztassuk meg a programot oly mdon, hogy az sszehasonltand mintt a parancssor els argumentuma adja meg. #define MAXLINE 1000 main (argc, argv) /*Megkeresi az 1. argumentum szerinti mintt*/ int argc; char *argv []; { char line [MAXLINE]; if (argc != 2) printf (Mintakeress \n); else while (getline (line, MAXLINE) > 0) if (index (line, argv [1]) >= 0) printf (%s, line); } Most kidolgozhatjuk az alapmodellt, amivel a tovbbi mutatalkalmazsokat szemlltethetjk. Tegyk fel, hogy kt opcionlis (szabadon vlaszthat) argumentumot engednk meg. Az egyik azt mondja,hogy nyomtass ki minden sort, kivve azokat, amelyek illeszkednek a mintra, mg a msodik azt mondja, rd minden kinyomtatott sor el annak sorszmt. A C programokban a mnusz jellel kezdd argumentumok megllapodsszeren opcionlis jelzt (flaget) vagy paramtert jelentenek. Ha teht az inverzi (a kivve, a fordtottsg) jellsre -x-et vlasztunk, s a sorszmozst -n-nel krjk, akkor a find -x -n the parancs a Now is the time for all good men to come to the aid of their party. bemeneti szveg esetn a 2: for all good men kimenetet fogja eredmnyezni. Az opcionlis argumentumok sorrendje tetszleges kell, hogy legyen, s a program tovbbi rsznek nem szabad fggenie a tnylegesen megadott argumentumok szmtl. Az adott esetben az index hvsnak nem szabad argv[2]-re hivatkoznia olyankor, amikor csupn egyetlen opcionlis argumentum volt, vagy argv[ 1 ]-re, ha egyltaln nem volt ilyen argumentum. A felhasznlk szmra knyelmes tovbb, ha az opcionlis argumentumok konkatenlhatk, mint pl.:

124

find -nx the me a program: #define MAXLINE 1000 main (argc, argv) /*Megkeresi az 1. argumentum szerinti mintt*/ int argc; char *argv []; { char line [MAXLINE], *s; long lineno = 0; int except = 0, number = 0; while (--argc > 0 && (*++argv) [0] == -) for (s = argv [0] + 1; *s != \0; s++) switch (*s) { case x: except = 1; break; case n: number = 1; break; default: printf (keress: illeglis opci %c \n, *s); argc = 0; break; } if (argc != 1) printf (Mintakeress: -x -n \n); else while (getline (line, MAXLINE) > 0) { lineno++; if ((index (line, *argv) >= 0) != except) { if (number) printf (%1d: , lineno); printf (%s, line); } } } argv minden egyes opcionlis argumentum eltt inkrementldik, argc pedig dekrementldik. Ha nincsenek hibk, akkor a ciklus vgn argc rtke 1 s *argv a mintra mutat. Vegyk szre, hogy *++argv argumentum-karakterlncot megcmz mutat: (*++argv) [0] az els karaktere. A zrjelek szksgesek, mivel nlklk a kifejezs *++ (argv[0]) lenne, aminek az rtelme teljesen ms (s rossz). Megengedett alak lenne mg *++argv is. 5.7. Gyakorlat.rjuk meg az add nev programot, amely kirtkel egy, a parancssorban szerepl fordtott lengyel alak kifejezst! Pldul add 2 3 4 + * 2 * (3+4)-et szmtja ki. 5.8. Gyakorlat. Mdostsuk az entab s detab programokat (amelyeket az 1. fejezetben gyakorlatknt rtunk meg) oly mdon, hogy az a tabultor stop-ok listjt argumentumokknt fogadja! Argumentum megadsnak hinyban a norml tabultorbelltsokat hasznljuk! 5.9. Gyakorlat. Bvtsk az entab s detab programokat gy, hogy elfogadjk az entab m +n rvidtett jellst, amely az m-edik oszloptl kezdve minden n-edik oszlopon egy-egy tabultor stop-ot jelent. Vlasszunk (a felhasznl szmra) knyelmes magatartst az alapesetre (default)! 5.10. Gyakorlat. rjuk meg a tail nev programot, amely kinyomtatja az utols n bemeneti sort! Az n alaprtelmezse legyen pl. 10, amit opcionlis argumentum vltoztathat meg, teht tail - n az

125

utols n sort nyomtatja ki. A programnak elfogadhatan kell viselkednie, fggetlenl attl, hogy mennyire rtelmetlen a bemenet vagy n rtke. Irjuk meg gy a programot, hogy a legjobban kihasznlja a rendelkezsre ll trterletet: a sorokat troljuk gy, mint a sort-ban, nem pedig rgztett mret ktdimenzis tmbben! 5.12.Fggvnyeket megcmz mutatk A C nyelvben maga a fggvny nem vltoz, de md vanfggvnyt megcmz mutat definilsra, amellyel mveletek vgezhetk, fggvnyeknek tadhat, tmbkbe helyezhet stb. Mindezt a fejezet korbbi rszben bemutatott rendezprogram mdostsval szemlltetjk: ha megadjuk a -n opcionlis argumentumot, akkor a program nem lexikografikusan, hanem numerikusan rendezi a bejv sorokat. A rendezs gyakran hrom rszbl ll - sszehasonltsbl, amely tetszleges objektumpr sorrendjt hatrozza meg, cserbl, amely megfordtja ezek sorrendjt s rendezalgoritmusbl, amely mindaddig vgzi az sszehasonltsokat s cserket, amg az objektumok a helyes sorrendbe nem kerlnek. Maga a rendezalgoritmus fggetlen az sszehasonltsi s felcserlsi mveletektl, gy klnbz sszehasonlt s felcserl fggvnyeket tadva klnbz kritriumok szerint rendezhetnk. Ezt az elvet alkalmazzuk az j rendezprogramban. A korbbiaknak megfelelen kt sor lexikografikus sszehasonltst az strcmp, felcserlst pedig a swap vgzi. Szksgnk lesz mg a numcmp rutinra, amely numerikus rtkk alapjn hasonlt ssze kt sort, s strcmp-hez hasonlan valamifle feltteljelzst ad vissza. Ezt a hrom fggvnyt a mainben deklarljuk, s az ket megcmz mutatkat adjuk t sortnak. A sort viszont a mutatkon keresztl hvja a fggvnyeket. Az argumentumokkal kapcsolatos hibafeldolgozst elhanyagoltuk, hogy a f feladatokkal foglalkozhassunk. #define LINES 100 /*A rendezend sorok maximlis szma*/ main (argc, argv) /*Beolvasott sorok rendezse*/ int argc; char *argv []; { char *lineptr [LINES]; /*Mutatk a szvegsorokra*/ int nlines; /*A beolvasott sorok szma*/ int strcmp (), numcmp (); /*sszehasonlt fggvnyek*/ int swap (); /*Felcserl fggvny*/ int numeric = 0; /*1, ha a rendezs numerikus*/ if (argc > 1 && argv [1][0] == - && argv [1][1] == n) numeric = 1; if ((nlines = readlines (lineptr, LINES)) >= 0) { if (numeric) sort(lineptr, nlines, numcmp, swap); else sort (nlineptr, nlines); } lines, strcmp, swap); writelines (lineptr,

} else printf (a bemenet tl nagy a rendezshez \n);

126

Az strcmp, numcmp s swap - fggvnyek cmei; mivel ezek bizonyosan fggvnyek, az & opertor ugyangy nem szksges, mint ahogy nincs r szksg a tmbk neve eltt sem. A fordt gondoskodik a fggvny cmnek tadsrl. Msodik lpsben a sort-ot mdostjuk: sort (v, n, comp, exch) /*A v[0] ... v[n-1] karakterlncok rendezse nvekv sorrendbe*/ char *v []; int n; int (*comp) () , (*exch) (); { int gap, i, j; for (gap = n / 2; gap > 0; gap /= 2) for (i = gap; i < n; i++) for (j = i - gap; j >= 0; j -= gap) { if ((*comp) (v [j], v [j + gap]) <= 0) break; (*exch) (&v [j], &v [j + gap]); } } Vizsgljuk meg a deklarcikat! int (*comp) () azt fejezi ki, hogy a comp olyan fggvnyt megcmz mutat, amely int-et ad vissza. Az els zrjelpr szksges, nlklk int *comp () azt fejezn ki, hogy a comp olyan fggvny, amely int-et megcmz mutatt ad vissza, ami egszen ms dolog. comp hasznlata az if ((*comp) (v [j],v [j + gap]) <= 0) sorban sszhangban van a deklarcival: comp a fggvnyt megcmz mutat, *comp a fggvny, s (*comp) (v [j], v [j + gap]) annak hvsa. Ahhoz. hogy az sszetevk helyesen kapcsoldjanak, szksg van a zrjelekre. Korbban mr lttuk strcmp-t, amely kt karakterlncot hasonlt ssze. me numcmp amely vezet numerikus rtkeik szerint hasonlt ssze kt karakterlncot : numcmp (s1, s2) /*s1 s s2 numerikus sszehasonltsa*/ char *s1 , *s2; { double atof (), v1 , v2; v1 = atof (s1); v2 = atof (s2); if (v1 < v2) return (-1); else if (v1 > v2) return (1); else return (0); }

127

Az utols lps a kt mutat felcserlst vgz swap fggvny megrsa. Ezt kzvetlenl arra alapozhatjuk, amit a fejezet korbbi rszben mr kzltnk: swap (px, py) { char *temp; temp = *px; *px = *py; *py = temp; } A rendezprogram tovbbi opcik egsz sorval egszthet ki; ezek kzl nhny rdekes gyakorlat lehet. 5.11. Gyakorlat. Mdostsuk a sort programot oly mdon, hogy kezelje -r-t, amely ellenttes irny (cskken) rendezst r el! -r-nek termszetesen -n-nel is mkdnie kell! 5.12. Gyakorlat. Illesszk hozz a -f opcit, amellyel egyestjk a kis- s nagybetket gy, hogy azokat a rendezs sorn nem klnbztetjk meg: a kis- s nagybets adatokat egytt rendezzk, teht a s A szomszdosknt jelennek meg, nem vlasztja el ket az egsz bc. 5.13. Gyakorlat. Illesszk a fggvnyhez a -d (sztri rendezs) opcit, amely csak betket, szmokat s szkzket hasonlt ssze! gyeljnk arra, hogy -f-fel egytt is mkdjn! 5.14. Gyakorlat. Egsztsk ki a fggvnyt mezkezel szolgltatssal, amely lehetv teszi, hogy sorokon bell kijellt mezkn is vgezhet legyen rendezs, mgpedig minden mezn egymstl fggetlen opcikszlet szerint! (E knyv angol eredetijnek trgymutatjt kulcsszavakra -dffel lapszmokra n-nel rendeztk.) _ 6. fejezet : Struktrk A struktra egy vagy tbb, esetleg klnbz tpus vltoz egyttese, amelyeket az egyszer kezelhetsg rdekben gyjtnk ssze. A struktrkat egyes nyelvekben, legismertebben a PASCAL-ban rekordoknak nevezik. A struktrt szemlltet hagyomnyos plda a brfizetsi lista: a dolgozt egy attributumkszlettel jellemezzk, amelyben helyet kap az illet neve, cme, trsadalombiztostsi szma, bre stb. Ezek nmelyike akr struktra is lehet: a nv, a cm vagy ppen a br maga is tbb rszbl llhat. A struktrk klnsen a nagy mret programok esetben nyjtanak segtsget bonyolult adathalmazok szervezsben, mivel sokszor lehetv teszik, hogy az sszetartoz vltozk egyttest egy egysgknt, nem pedig klnll elemekknt kezeljk. Ebben a fejezetben megksreljk bemutatni a struktrk hasznlatt. Mintaprogramjaink a knyvnkben megszokottaknl terjedelmesebbek lesznek, de mg mindig elgszerny mretek. 6.1. Alapfogalmak Vegyk ismt el az 5. fejezetben trgyalt dtumkonverzis rutinokat. Egy-egy adat tbb rszbl ll, ilyenek a nap, a hnap s az v, tovbb esetleg a nap sorszma az vben, s a hnap neve. Ez az t vltoz az albbi mdon egyetlen struktrba foglalhat: /* *px s *py felcserlse*/ char *px [], *py [];

128

struct

date {

int day; int month; int year; int yearday; char mon_name [4]; } A struct kulcssz a struktra deklarcijt vezeti be, amely nem ms, mint egy kapcsos zrjelek kz zrt deklarcilista. A struct kulcsszt struktracmke kvetheti (mint pl. az elbb a date). Ez egy nv, amely megnevezi az adott tpus struktrt, s a tovbbiakban rvidtsknt hasznlhat a rszletes deklarci helyett. A struktrban elfordul elemeket, ill. vltozatokat tagoknak nevezzk. Valamely struktratagnak, ill. -elemnek s egy kznsges (vagyis nem tag) vltoznak lehet ugyanaz a neve: ebbl nem szrmazik zavar, mivel ezek a szvegkrnyezet alapjn mindig megklnbztethetk. Az mr termszetesen stlus krdse, hogy az ember ugyanazokat a neveket csak szorosan sszetartoz objektumok esetben hasznlja. A tagok listjt lezr jobboldali zrjelet a vltozlista kvetheti ugyangy, mint minden alaptipusnl. Eszerint struct { . . . } x, y, z; szintaktikusan hasonl az int x, y, z; sorhoz abban az rtelemben, hogy mindkt utasts a megnevezett tipus vltozknt deklarlja x-et, y-t s z-t, ill. helyet foglal szmukra. Az olyan struktradeklarci, amit nem kvet vltozlista, nem foglal trhelyet, csupn a struktra alakjt (template) rja le. Ha azonban a deklarci cimkzett, akkor ez a cimke a ksbbiekben a struktra konkrt elfordulsakor a defincikban hasznlhat. Ha pl. adott a date elbbi deklarcija, akkor struct date d; gy definilja a d vltozt, hogy az date tipus struktra legyen. A kls, ill. a statikus struktra gy inicializlhat, hogy defincijt az elemek kezdeti rtkeibl ll lista kveti: struct date d = {4, 7, 1776, 186, jl}; Valamely struktra adott tagjra kifelyezsen bell a struktranv.tag alak szerkezettel lehet hivatkozni. A . struktratag opertor a struktra nevt s a tag nevt kapcsolja ssze. Ha pl. a d struktrabeli dtum alapjn akarjuk leap-et (a szkvet) belltani, akkor a kd leap = d.year % 4 == 0 && d.year % 100 != 0 || d.year % 400 == 0; lesz. A hnap nevnek vizsglata: if (strcmp (d.mon_name, aug) == 0) . . . Ha a hnapneveket az angol helyesrs szerint nagy kezdbetkkel rtuk volna, kisbetss alaktsuk a kvetkezkppen trtnhetne: d.mon_name [0] = lower (d.mon_name [0]); A struktrk egymsba skatulyzhatk; a fizetsi jegyzk pl. gy nzhet ki: struct person {

129

char name [NAMESIZE]; char address [ADRSIZE]; long zipcode; long ss_number; double salary; struct date birthdate; struct date hiredate; }; A person nev struktra kt dtumot tartalmaz. Ha az emp-et mint struct person emp; deklarljuk, akkor emp.birthdate.month a szlets hnapjra vonatkozik. A .struktratag opertor balrl jobbra kt. 6.2. Struktrk s fggvnyek A C nyelvbeli struktrkra szmos megkts vonatkozik. Ezek kzl a leglnyegesebb, hogy struktrn csak ktfle mvelet vgezhet a struktra cmnek ellltsa az & szimblum hasznlatval, s a struktra valamelyik tagjhoz val hozzfrs. Ebbl kvetkezleg a struktrkat egy egysgknt nem lehet semmihez sem hozzrendelni (rtkl adni), ill. msolni, nem adhatk t fggvnyeknek, s a fggvnyek sem adhatnak vissza struktrkat. (A C nyelv ksbb megjelen vltozataibl ezek a megktsek ki fognak maradni.) A struktrkat megcimz mutatkra mr nem vonatkoznak ezek a korltozsok, gy a struktrk s a fggvnyek knyelmesen egytt tudnak mkdni. Vgezetl, az automatikus tmbkhz hasonlan az automatikus struktrk sem inicializlhatk, ez csak a kls s a statikus struktrk esetben lehetsges. Vizsgljunk meg e jellegzetessgek kzl nhnyat! Pldaknt rjuk t az elz fejezetben ltott dtumkonverzis rutint, struktrk hasznlatval! Mivel a szablyok nem engedik meg, hogy struktrt fggvnynek kzvetlenl tadjunk, vagy kln-kln az elemeket, vagy az egszt megcmz mutatt kell tadnunk. Az els lehetsg gy hasznlja day_of_year-t, ahogy az 5. fejezetben lertuk: d.yearday = day_of_year(d.year, d.month, d.day); A msik lehetsg a mutattads. Ha a hiredate-et struct date hiredate; alakban deklarltuk s day_of_year-t trtuk, akkor hiredate.yearday = day_of_year (&hiredate); segtsgvel a hiredateet megcmz mutatt tadhatjuk day_of_year-nek. A fggvnyt mdostani kell, mivel argumentuma a korbbi vltozlista helyett most mutat lett: day_of_year (pd) /*Az v napjnak ellltsa a hnapbl s a napbl*/ struct date *pd; { int i, day, leap; day = pd->day; leap = pd->year % 4 == 0 && pd->year % 100 != 0 || pd->year % 400 == 0; for (i = 1; i < pd->month; i++) day += day_tab [leap][i]; return (day); } A struct date *pd; deklarci szerint pd olyan mutat, amely date tpus

130

struktrra mutat. A pd->year plda szerinti jellsmd j. Ha p struktrt megcmz mutat, akkor p->struktratag az adott tagra vonatkozik. mnuszjelbl s az azt kvet >-bl ll.) Mivel pd a struktrra mutat, a year tagra a (*pd).year alakban is hivatkozhatunk, de struktrkat jell mutatkat olyan gyakran hasznlunk, hogy knyelmes rvidtsknt a -> jellsmd is rendelkezsre ll. A (*pd).year alakban a zrjelek szksgesek, mivel a . struktratag opertor precedencija magasabb, mint a * opertor. Mind ->, mind pedig . balrl jobbra kt, gy p->q->memb emp.birthdate.month rtelme : (p->)->memb (emp.birthdate).month A teljessg kedvrt me a msik fggvny, month_day, amit szintn a struktra hasznlatval runk t : month_day (pd) /*Hnap s nap ellltsa az v napjbl*/ struct date *pd; { int i, leap; leap = pd->year % 4 == 0 && pd->year % 100 != 0 || pd->year % 400 == 0; pd->day = pd->yearday; for (i = 1; pd->day > day_tab [leap][i]; i++) pd->day -= day_tab [leap][i]; pd->month = i; } A -> s . struktraopertorok, valamint az argumentumlistkat kzrefog () s az indexet tartalmaz [] a precedenciahierarchia cscsn ll, s ezrt nagyon szorosan kt. Ha pl. adott a struct } *P; deklarci, akkor ++p->x nem p-t, hanem x-et inkrementlja, mivel az alaprtelmezs szerinti zrjelezs: ++(p->x) Zrjelek hasznlatval a kts megvltoztathat: (++p)->x az x-hez val hozzfrs eltt nveli p-t, mg (p++)->x azt kveten inkrementl. (Az utbbi zrjelkszlet felesleges. Mirt?) { int x; int *y; (A -> opertor

131

Hasonlkppen, *p->y behozza, amire p mutat; *p->y++ azutn inkrementlja y-t, miutn hozzfrt ahhoz, amire mutat (ppgy, mint *s++); (*p->y) ++ azt nveli, amire y mutat, mg *p++->y azutn inkrementlja p-t, hogy hozzfrt ahhoz, amire y mutat. 6.3. Struktratmbk A struktrk klnsen alkalmasak egymssal sszefgg vltozk tmbjeinek kezelsre. Tekintsk pl. azt a programot, amely a C nyelv kulcsszavainak elfordulsait szmllja. A nevek trolshoz szksgnk lesz egy karakterlnc-tmbre, a darabszmok trolshoz pedig egy egszekbl ll tmbre. Az egyik megolds szerint prhuzamosan kt tmbt hasznlunk. Legyenek ezek pl. keyword s keycount: char *keyword [NKEYS]; int keycount [NKEYS]; Azonban maga a tny, hogy lehetsgnk van msfajta kulcsszbejegyzs egy pr: char *keyword; int keycount; Hozzuk ltre a prok tmbjt! A struct key { char *keyword; int keycount; } keytab [NKEYS]; struktradeklarci az ilyen tpus struktrk keytab nev tmbjt definilja s trterletet foglal le szmra. A tmb minden eleme struktra. Ezt gy is rhatjuk: struct key { char *keyword; int keycount; }; struct key keytab [NKEYS]; Mivel a keytab struktra jelen esetben a nevek lland halmazt tartalmazza, legegyszerbb, ha definilskor egyszer s mindenkorra inicializljuk. A struktrk inicializlsa mindenben hasonlt a korbbiakhoz - a defincit a kezdeti rtkek kapcsos zrjelek kz zrt listja kveti: struct key { char *keyword; int keycount; } keytab [] = { break, 0, case, 0, char, 0, continue, 0, default, 0, /* ... */ unsigned , 0, while, 0, }; A kezdeti rtkeket a struktratagoknak megfelelen pronknt soroltuk fel. Pontosabb lenne, ha minden sor vagy struktra kezdeti rtkeit zrnnk kapcsos zrjelek kz {break, 0},

a tmbk prhuzamosak, jelzi, hogy szervezsre is. Valjban minden

132

{ case, 0}, . . . szerint, de a bels kapcsos zrjelekre nincs szksg, ha a kezdeti rtkek egyszer vltozk vagy karakterlncok, mint a jelen esetben is. Amennyiben kezdeti rtkek vannak megadva, s a [] resen maradt, a fordt most is ezek alapjn szmtja ki a keytab tmb elemeinek szmt. A kulcssz-szmll program a keytab definilsval kezddik. A frutin a getword fggvny ismtelt hvsval szavanknt olvassa a bemenetet. A program minden szt megkeres a keytab-ben a binris keresfggvnynek a 3. fejezetben ltott vltozatval. (A helyes mkds rdekben a kulcsszavak listjt termszetesen nvekv sorrendben kell megadnunk.) #define MAXWORD 20 main () /*C kulcsszavak szmllsa*/ { int n, t; char word [MAXWORD]; while ((t = getword (word, MAXWORD)) != EOF) if (t == LETTER) if ((n = binary (word, keytab, NKEYS)) >= 0) keytab [n].keycount+ +; for (n = 0; n < NKEYS; n++) if (keytab [n].keycount > 0) printf (%4d %s \n, keytab [n].keycount, keytab [n].keyword); } binary (word, tab, n) /*Sz megkeresse tab[0] . . . tab[n-1]-ben*/ char *word; struct key tab []; int n; { int low, high, mid, cond; low = 0; high = n - 1; while (low <= high) { mid = (low + high) / 2; if ((cond = strcmp (word, tab [mid].keyword)) < 0) high = mid - 1; else if (cond > 0) low = mid + 1; else return (mid); } return (-1); } Rvidesen sor kerl a getword fggvny bemutatsra; egyelre elg annyit tudnunk, hogy minden alkalommal, amikor megtall egy kulcsszt, a LETTER rtket adja vissza s az illet szt az els argumentumba msolja. Az NKEYS mennyisg a kulcsszavak szma a keytab-ban. Br ezt magunk is megszmllhatnnk, sokkal knnyebb s biztonsgosabb, ha a gpre bzzuk, klnsen, ha a lista vltozhat. Az egyik lehetsg az lenne,

133

hogy a kezdeti rtkek listjt a nulla mutatval zrjuk le, majd ciklusban addig haladunk keytab-on, amg a vgt meg nem talltuk. Ez azonban tbb, mint amire szksg van, mivel a fordt a tmb mrett fordts kzben pontosan meghatrozza. A bejegyzsek szma ebbl: keytab mrete / struct key mrete A C-ben rendelkezsnkre ll a sizeof egyoperandus opertor, amelynek segtsgvel brmilyen objektum mrete fordtsi idben meghatrozhat. A sizeof (objektum) kifejezs eredmnye olyan egsz szm, amely megegyezik a megadott objektum mretvel. (A mretet byte-nak nevezett specifiklatlan egysgekben kapjuk, amelynek mrete ugyanaz, mint egy char-.) Az objektum valamilyen aktulis vltoz, tmb vagy struktra, vagy pedig valamilyen alap-, ill. leszrmaztatott tipus (l. int vagy double, ill. struktrk) neve lehet. Esetnkben a kulcsszavak szma a tmbmret osztva egy tmbelem mretvel. Ezt a szmtst #define utastsban hasznlva, lltjuk be az NKEYS rtkt: #define NKEYS (sizeof (keytab) / sizeof (struct key)) Most pedig nzzk a getword fggvnyt. A getword-nek az adott program szmra szksgesnl ltalnosabb vltozatt rtuk meg, amely azonban nem lnyegesen bonyolultabb, getword a bemeneten soron kvetkez szt adja vissza, ahol a sz vagy betk s szmok betvel kezdd lnca, vagy pedig egyetlen karakter. Az objektum tipust fggvnyrtkknt kapjuk meg; ez az rtk LETTER, ha az adott egysg sz, EOF az llomny vgn, vagy maga a karakter, ha az nem alfabetikus. getword (w,lim) /*Vedd a kvetkez szt a bemenetrl*/ char *w; int lim; { int c,t; if (type (c = *w++ = getch ()) != LETTER) { *w = \0; return ; } while (--lim > 0) { t = type (c = *w++ = geth ()); if (t != LETTER && t != DIGIT) { ungetch ; break; } } *(w - 1) = \0; return (LETTER); } A getword a getch s ungetch rutinokat hasznlja, amelyeket a 4. fejezetben rtunk meg. Amikor egy alfabetikus szvegegysg begyjtse befejezdik, a getword mr a szksgesnl egy karakterrel tbbet olvasott be. Az ungetch hvsval ezt a karaktert a getword kvetkez hvsig visszarjuk a bemenetre. A getword a type hvsval llaptja meg az egyes bemeneti karakterek tpust. Az albbi vltozat csak az ASCII karakterkszletben mkdik: type { /*ASCII karakter tpusnak visszaadsa*/ int c;

134

if (c >= a && c <= z || c >= A && c <= Z) return (LETTER); else if (c >= 0 && c <= 9) return (DIGIT); else return ; } A LETTER s a DIGIT szimbolikus lland minden olyan rtket felvehet, amely nem tkzik a nem-alfanumerikus karakterekkel s az EOF-fal, kzenfekv pl. az albbi vlaszts: #define LETTER a #define DIGIT 0 A getword-t felgyorsthatjuk, ha a type fggvny hvsait valamely alkalmas type [] tmbre vonatkoz hivatkozsokkal helyettestjk. A szabvnyos C knyvtrban rendelkezsnkre llnak az isalpha s isdigit nev makrk, amelyek ily mdon mkdnek. 6.1. Gyakorlat. Vgezzk el a getword-nek ezt a mdostst, s mrjk meg a program sebessgnek vltozst! 6.2. Gyakorlat. rjuk meg a type olyan vltozatt, amely fggetlen a karakterkszlettl! 6.3. Gyakorlat. Irjuk meg a kulcsszszmll program olyan vltozatt, amely nem szmllja az idzjelek kz zrt karakterlncokban elfordul kulcsszavakat! 6.4. Struktrkat megcmz mutatk A mutatkkal s struktratmbkkel kapcsolatos megfontolsaink szemlltetsre rjuk jra a kulcsszszmll programot, ezttal tmbindexek helyett mutatk hasznlatval! A keytab kls deklarcijt nem kell megvltoztatnunk, a main s a binary azonban mdostsra szorul. main () /*C kulcsszavak szmllsa; mutatt alkalmaz vltozat*/

{ int t; char word [MAXWORD]; struct key *binary (), *p; while ((t = getword (word, MAXWORD)) != EOF) if (t == LETTER) if ((p = binary (word, keytab, NKEYS)) != NULL) p->keycount++; for (p = keytab; p < keytab + NKEYS; p++) if (p->keycount > 0) printf (%4d %s \n, p->keycount, p->keyword); } struct key *binary (word, tab, n) /*Sz keresse*/ char *word; /*tab [0] . . . tab [n-1]-ben*/ struct key tab []; int n; { int cond; struct key *low = &tab [0]; struct key *high = &tab [n - 1]; struct key *mid; while (low <= high) { mid = low + (high - low) / 2; if ((cond = strcmp (word, mid->keyword)) < 0) high = mid - 1; else if (cond > 0) low = mid + 1;

135

else return (mid); } return (NULL); } Ebben a programban tbb dolog is emltsre mlt. Elszr is binary deklarcijnak jeleznie kell, hogy key tpus struktrt megcmz mutatt ad vissza, s nem egsz tpus mennyisget; ezt mind a mainben, mind binaryban deklarltuk. Ha binary megtallta a szt, az azt kijell mutatt adja vissza; ha a keress eredmnytelen, a visszaadott rtk NULL. Msodszor, a keytab elemeihez trtn minden hozzfrs mutatkkal trtnik. Emiatt a binary rutin jelentsen megvltozik: a kzps elem kiszmtsa mr nem lehet egyszeren mid = (low + high) / 2 mivel kt mutat sszeadsa semmifle rtelmes vlaszt nem eredmnyez (mg a 2-vel val osztskor sem), st ez a mvelet tiltott ! Ehelyett a mid = low +(high - low) / 2 alakra van szksg, amely gy lltja be mid-et, hogy az a low s a high kztti terlet felezpontjra mutasson. Figyeljk meg low s high kezdeti rtkeit is! Lehetsg van arra, hogy egy mutatt valamely korbban definilt objektum cmvel inicializljunk: itt ppen ezt tettk. A main-ben azt rtuk, hogy for (p = keytab; p < keytab + NKEYS; p++) Ha p struktrt megcmz mutat, akkor minden p-re vonatkoz aritmetikai szmts figyelembe veszi a struktra tnyleges mrett, gy p++ a p-t a megfelel mennyisggel inkrementlja ahhoz, hogy ellljon a struktrk tmbjnek kvetkez eleme. Ne higyjk azonban, hogy a struktra mrete megegyezik az elemei mretnek az sszegvel - a klnbz jelleg objektumok elhelyezkedsi kvetelmnyeinek folytn lyukak lehetnek a struktrban. Vgezetl egy megjegyzs a program alakjval kapcsolatban. Ha egy fggvny bonyolult tpust ad vissza, mint a struct key *binary(word, tab, n) esetben, akkor a fggvny nevt esetleg nehz szrevenni, vagy szvegszerkeszt programmal (text editorral) megtallni. Emiatt nha ms formt szoks hasznlni: struct key binary (word, tab, n) Ez termszetesen leginkbb szemlyes zls krdse: vlasszuk ki a neknk tetsz alakot, s ahhoz tartsuk magunkat. 6.5. nhivatkoz struktrk Tegyk fel, hogy ltalnosabb feladatknt a bemeneti szveg sszes szavnak elfordulsait akarjuk megszmllni. Mivel a szavak listja elzetesen nem ismert, azt nem tudjuk alkalmas mdon rendezni s nem hasznlhatunk binris keresst. Lineris keresst azonban vgre tudunk hajtani minden berkez szra, amivel megnzzk, hogy volt-e mr ilyen sz: a program futsa azonban gy rkk fog tartani. (Pontosabban szlva a vrhat futsi id a

136

beolvasott szavak szmval ngyzetesen n.) Hogyan szervezzk meg az adatokat ahhoz, hogy hatkonyan meg tudjunk brkzni a tetszleges szavakbl ll listval? Az egyik megolds szerint llandan rendezett llapotban tartjuk a mr megvizsglt szavakat oly mdon, hogy a berkezs sorrendjben minden szt a neki megfelel helyre tesznk. Ezt azonban nem gy vgezzk el, hogy a szavakat egy lineris tmbben tologatjuk, mivel ez is tl sokig tartana. Ehelyett a binris fa nev adatstruktrt fogjuk hasznlni. A fa minden klnbz szhoz egy-egy csompontot rendel, amelynek tartalma : a sz szvegt megcmz mutat, a sz elfordulsainak szma, a bal oldali gyermek (leszrmazott) csompontot megcmz mutat, a jobb oldali gyermek csompontot megcmz mutat. Egyetlen csompontnak sem lehet kettnl tbb gyermeke; lehet viszont nulla vagy egy gyermeke. A csompontokat gy hozzuk ltre, hogy minden egyes csompont esetben a bal oldali rszfa csupa olyan szt tartalmaz, amely kisebb, mint a csompontbeli sz, mg a jobb oldali rszfban csupa olyan sz van, amely nla nagyobb. Ha el akarjuk dnteni, hogy egy j sz mr rajta van-e a fn, a vizsglatot a fa gykernl kezdjk, s az j szt az illet csompontban trolt szval hasonltjuk ssze. Ha megegyeznek, a vlasz igenl. Ha az j sz kisebb, mint a csompontbeli sz, a keress a bal oldali, ellenkez esetben a jobb oldali gyermek csompontban folytatdik. Ha a kivlasztott irnyban nincs leszrmazott, a sz nincs a fn, s ppen a hinyz leszrmazottnak megfelel csompontba kell bernunk. Ez a keressi eljrs rekurzv, hiszen brmelyik csomponttl indul keress tartalmazza a valamelyik leszrmazottjtl indul keresst is. Ennek megfelelen a legtermszetesebb az, ha a beillesztsre s kirsra is rekurzv rutinokat hasznlunk. Visszatrve a csompont lersra, lesz, amely ngy sszetevbl ll: a csompont nyilvn struktra

struct tnode { /*Alapcsompont*/ char *word; /*A szvegre mutat*/ int count; /*Elfordulsok szma*/ struct tnode *left; /*Bal oldali gyermek*/ struct tnode *right; /*Jobb oldali gyermek*/ } A csompontnak ez a rekurzv deklarcija taln bizonytalanul fest, de valjban teljesen helyes s pontos. A struktra nem tartalmazhatja sajt megnevezst, de struct tnode *left; a left-et a csompontot megcmz mutatnak, nem pedig csompontnak deklarlja. Az egsz program meglepen rvid, mivel mr korbban megrt segdrutinokat hasznl. Ezek: a getword, amellyel az egyes bemeneti szavakat olvassuk be, s az alloc, amellyel helyet biztostunk a szavak arrbbcssztatshoz. A frutin egyszeren a getword segtsgvel beolvassa a szavakat s a tree hasznlatval elhelyezi azokat a fn. #define MAXWORD 20

137

main ()

/*Szavak gyakorisgnak szmllsa*/

{ struct tnode *root, *tree (); char word [MAXWORD]; int t; root = NULL; while ((t = getword (word, MAXWORD)) != EOF) if (t == LETTER) root = tree (root, word); treeprint (root); } Maga a tree egyszer. A main a fa tetejn (a gykr szintjn egy szt ad t. Ezt a szt minden lpsben sszehasonltjuk az adott csompontnl mr trolt szval s a tree rekurzv hvsai rvn vagy a bal oldali, vagy a jobb oldali rszfa irnyban haladunk tovbb. Vgl is a sz vagy megegyezik valamelyik, mr a fn trolt szval (amikor is elfordulsainak szmt nveljk), vagy pedig a program a nullamutatt tallja meg, ami azt jelzi, hogy j csompontot kell ltrehozni s a ft azzal ki kell bvteni. Uj csompont ltrehozsakor a tree az azt megcmz mutatval tr vissza, ami bekerl a szl csompontba: struct tnode *tree (p, w) /*w elhelyezse p-nl vagy p alatt*/ struct tnode *p; char *w; { f struct tnode *talloc (); char *strsave (); int cond; if (p == NULL) { /*j sz rkezett*/ p = talloc (); /*j csompont kszl*/ p->word = strsave (w); p->count = 1; p->left = p->right = NULL; } else if ((cond = strcmp (w, p->word)) == 0) p->count++; /*Mr volt ilyen sz*/ else if (cond < 0) /*Kisebb - a bal rszfba kerl*/ p->left = tree (p->left, w); else } Az j csompont szmra szksges trhelyet a talloc szolgltatja, amely a mr korbban megrt alloc mdostott vltozata. A talloc rutin a fa csompontjnak trolsra alkalmas szabad terletet megcmz mutatt ad vissza. (errl rviden rszletesebben is szlunk.) Az j szt az strsave msolja be egy rejtett hejre, a darabszm inicializldik, s a kt leszrmazott nulla lesz. A programkdnak ezt a rszt csupn a fa szlein hajtjuk vgre, amikor j csompontot iktatunk be. A strsave s a talloc ltal visszaadott rtkek hibaellenrzst elhagytuk (ami lesben hasznlt program esetben nem blcs dolog). A treeprint a ft a bal oldali rszfa sorrendjben nyomtatja ki; minden egyes csompontnl kinyomtatja a bal oldali rszft (minden olyan szt, amely a krdses sznl kisebb), majd magt a szt s vgl a jobb oldali rszft (minden olyan szt, amely nagyobb). Ha az /*Nagyobb - a jobb rszfba kerl*/ p->right = tree (p->right, w); return (p);

138

olvas bizonytalannak rzi magt a rekurzis technikval kapcsolatban, rajzoljon le egy ft s nyomtassa ki a treeprint-tel: kevs ennl ttekinthetbb rekurzv rutint tallhatunk. treeprint (p) /*A p fa rekurzv kinyomtatsa*/ struct tnode *p; { if (p != NULL) { treeprint (p->left); printf (%4d %s \n, p->count, p->word); treeprint (p->right); } } Gyakorlati megjegyzs: ha a fa kiegyenslyozatlann vlik, mert a szavak nem vletlenszer sorrendben rkeznek, a program futsi ideje tl gyorsan nvekedhet. A legrosszabb eset az, amikor a szavak mr sorrendben vannak, ilyenkor ez a program igen kltsges mdon szimullja a lineris keresst. A binris fnak vannak ltalnostsai, mgpedig a 2-3 fk s az AVL fk, amelyek mentesek ettl a legrosszabb esetben bekvetkez viselkedstl, knyvnkben azonban ezeket nem ismertethetjk. Mieltt befejeznnk a pldt, rdemes rvid kitrt tennnk a trterlet-lefoglalssal kapcsolatos egyik problmra. Nyilvn j lenne, ha a programban csak egy trfoglal fggvny lenne, mg akkor is, ha az klnfle jelleg objektumok szmra foglal helyet. Ha azonban ugyanaz a fggvny foglal helyet pl. char-okat s struct tnode-okat megcmz mutatk szmra, kt krds merl fel. Elszr is, hogyan elgti ez ki a legtbb valsgos gpnek azt a kvetelmnyt, hogy bizonyos tpus objektumok adott elhelyezsi elrsoknak kell, hogy eleget tegyenek? (pl. az egsz tpus mennyisgeket gyakran pros cmen kell elhelyezni.) Msodszor, milyen deklarcik tudnak megbrkzni azzal a tnnyel, hogy az alloc szksgszeren klnbz tpus mutatkat ad vissza? Az elhelyezsi elrsoknak ltalban - nmi hely elvesztse rn egyszeren gy tehetnk eleget, ha gondoskodunk arrl, hogy a helyfoglal mindig olyan mutatt adjon vissza, amely az sszes elhelyezsi kvetelmnynek eleget tesz. A PDP-11-en pl. elegend, ha az alloc mindig pros mutatt ad vissza, mivel pros cmen brmilyen tpus objektum trolhat. Ennek ra csupn egyetlen elvesztett karakterpozci pratlan_ hosszsg mennyisg esetn. Hasonl intzkeds tehet ms gpeken is. gy lehet, hogy az alloc megvalstsa nem gpfggetlen, a hasznlata azonban az. Az 5. fejezetben ismertetett alloc semmifle megklnbztetett elhelyezkedst sem garantl, a 8. fejezetben bemutatjuk, hogyan kell helyesen megoldani ezt a feladatot. Az alloc tpusdeklarcijnak krdse minden olyan nyelvben gondot okoz, amely komolyan veszi a tpusellenrzst. A C-ben a legjobb eljrs az, ha az alloc-ot gy deklarljuk, hogy char-t megcmz mutatt adjon vissza, majd tpusmdost szerkezettel explicit knyszerrel vltoztatjuk a mutatt a kvnt tpusv. Ha teht p deklarcija char *p; akkor (struct tnode *)p egy kifejezsben p-t tnode mutatv alaktja t. gy a talloc lersa: struct tnode *talloc ()

139

{ char *alloc (); return ((struct tnode *) alloc (sizeof (struct tnode))); } Ez mr tbb, mint amire a jelenlegi fordtk esetben szksg van, azonban jelzi a jvre nzve a legbiztosabb irnyt. 6.4. Gyakorlat. rjunk olyan programot, amely beolvas egy C programot, s alfabetikus sorrendben kinyomtatja a vltozneveknek azokat a csoportjait, amelyek els 7 karakterkben megegyeznek, azonban ezt kveten valahol klnbznek! gyeljnk arra, hogy a 7 paramter legyen! 6.5. Gyakorlat. rjunk elemi, keresztbe hivatkoz (crossreferencing) programot, amely kinyomtatja egy dokumentumban elfordul szavak listjt, s minden szra megadja azoknak a soroknak a sorszmt, ahol az illet sz elfordul! 6.6. Gyakorlat. rjunk olyan programot, amely a bemenetn elfordul szavakat az elforduls cskken sorrendjbe rendezve nyomtatja ki! Minden sz el rjuk oda az elfordulsok szmt! 6.6. Keress tblban Ebben a fejezetben egy tblakeres (table-lookup) programcsomag belsejt rjuk meg, amivel a struktrk tovbbi vonatkozsait illusztrljuk. Tipikusan ilyen programkd tallhat a makroprocesszorok vagy fordtk szimblumtbla-kezel rutinjaiban. Tekintsk pl. a C #define utastst. Ha egy olyan sor, mint #define YES 1 fordul el, a YES nv s az 1 helyettest szveg bekerl egy tblzatba. A ksbbiekben, amikor a YES nv utastsokban fordul el, pl. inword = YES; azt 1 -gyel kell helyettesteni. Kt frutin kezeli a neveket s a helyettest szvegeket. Az install(s, t) berja az s nevet s a t helyettest szveget egy tblzatba; az s s a t egyszeren karakterlncok. A lookup(s) megkeresi s-et a tblzatban, s egy mutatt ad vissza, amely arra a helyre mutat, ahol s-et megtallta, vagy pedig NULL, ha s nincs a tblzatban. Az n. hash keressi algoritmust hasznljuk - a program a berkez nevet kis pozitv egsz szmm alaktja t, amelyet ksbb azutn egy mutattmb indexelsre hasznl. A tmb egy eleme olyan blokkok lncnak kezdetre mutat, amelyek az illet hash rtk neveket rjk le. A tmbelem NULL, ha egyetlen nv sem rendelkezik az adott hash rtkkel. A lncban egy blokk olyan struktra, amely a nevet megcmz mutatkat, a helyettest szveget s a kvetkez lncbeli blokkot megcmz mutatt tartalmazza. A lnc vgt a kvetkez blokk mutatjnak nulla rtke jelzi : struct nlist { /*Elemi tbla bejegyzs*/ char *name; char *def; struct nlist *next; lncban*/ /*Kvetkez bejegyzs a

140

} A mutattmb: #define HASHSIZE 100 static struct nlist *hashtab [HASHSIZE]; /*Mutattbla*/ A lookup s az install ltal egyarnt hasznlt hash rtkkpz fggvny egyszeren sszeadja a lncbeli karakterrtkeket s kpezi az sszegnek a tmbmrettel vett maradkt. (Ez nem a lehet legjobb algoritmus, de megvan az az elnye, hogy rendkvl egyszer.) hash (s) /*Az s string hash rtknek kpzse*/ char *s; { int hashval; for (hashval = 0; *s != \0; ;) hashval += *s++; return (hashval % HASHSIZE); } A hash-eljrs a hashtab _tmbben ltrehoz egy kezdindexet; ha a karakterlnc egyltaln megtallhat, akkor az itt kezdd blokklncban kell lennie. A keresst a lookup vgzi. Ha lookup megtallja a bejegyzst, a megfelel mutatt adja vissza; ha nem, akkor NULL-lal tr vissza. struct nlist *lookup (s) /*s keresse hashtab-ben*/ char *s; { struct nlist *np; for (np = hashtab [hash (s)]; np != NULL; np = np->next) if (strcmp (s, np->name) == 0) return (np); return (NULL); } Az install a lookup-ot hasznlja annak eldntsre, hogy a belltott nv mr jelen van-e. Ha igen, akkor az j definci fellbrlja a rgit, egybknt pedig teljesen j bejegyzs keletkezik. Az install NULL-t ad vissza, ha valamilyen oknl fogva nincs hely az j bejegyzs szmra. struct nlist *install (name, def)/*(name, def) elhelyezse hashtab-ben*/ char *name, *def; { struct nlist *np, *lookup (); char *strsave (), *alloc (); int hasval; if ((np = lookup (name)) == NULL) { /*Nem tallta (struct nlist *) alloc (sizeof (np)); if (np == NULL) return (NULL); hashval = hash( np->name); [hashval]; hashtab [hashval] = np; /*Megtallta*/ /*Nem tallta meg*/

meg*/

np

return (NULL); if ((np->name = strsave (name)) == NULL) np->next = hashtab

} else /*Mr ott van*/ free (np->def); /*Felszabadtja az elz defincit*/ if ((np->def (strsave (def))) == NULL) return (NULL); return (np);

141

} Az strsave egyszeren tmsolja az argumentumban megadott karakterlncot valamilyen biztos helyre, amit az alloc hvsval kapott. Ezt a programot az 5. fejezetben lttuk. Mivel az alloc s a free hvsai tetszleges sorrendben elfordulhatnak, tovbb minthogy az elhelyezkeds is szmt, az alloc-nak az 5. fejezetben kzlt egyszer vltozata itt nem elegend (l. a 7. s 8. fejezetet). 6.7. Gyakorlat. rjunk olyan rutint, amely a lookup s install ltal kezelt tblbl trl egy nevet s egy defincit! 6.8. Gyakorlat. Az ebben a fejezetben kzlt rutinokat, ill. getcht s ungetch-t alapul vve valstsuk meg a #define processzor egyszer vltozatt, amely C programok szmra hasznlhat! 6.7. Mezk Ha szkben vagyunk a trhelynek, elfordulhat, hogy tbb objektumot egyetlen gpi szban kell elhelyeznnk. Tipikus esete ennek az egybites feltteljelzk (flagek) alkalmazsa, pl. a fordtprogramok szimblumtbliban. Kvlrl knyszertett adatformtumok, pl. hardvereszkzk illesztsekor, gyakran ignylik azt a lehetsget, hogy a gpi sz egyes darabjaihoz is hozzfrhessnk. Kpzeljk el a fordtnak azt a rszt, amely a szimblumtblt kezeli. Minden programbeli azonosthoz bizonyos informci trsul, pl. hogy kulcssz-e vagy sem, hogy kls s/vagy statikus stb. mennyisgrl van-e sz. Az ilyen informci kdolsnak legtmrebb mdja az egybites feltteljelzk kszletnek hasznlata egyetlen char-on vagy int-en bell. Ez ltalban gy trtnik, hogy a vlasztott bitpozciknak megfelelen egy maszk-kszletet definilnak, mint #define KEYWORD 01 #define EXTERNAL 02 #define STATIC 04 (A szmoknak kett hatvnyainak kell lennik.) Ezek utn a biteket a 2. fejezetben ismertetett lptet, maszkol s komplementl opertorokkal mr knnyen elrhetjk. Bizonyos fordulatok klnsen gyakoriak: flags = EXTERNAL | STATIC; 1-re lltja a flags-ben az EXTERNAL s STATIC biteket, mg flags &= ~(EXTERNAL | STATIC); ugyanezeket a biteket kinullzza, s if ((flags &(EXTERNAL | STATIC)) == 0) . . . nulla. igaz, ha mindkt bit

Br ez a forma gyorsan elsajtthat, a C nyelv azt is lehetv teszi, hogy valamely szn bell ne bitenknti logikai opertorokkal, hanem kzvetlenl definiljunk s rjnk el egyes mezket. A mez (field) szomszdos bitek halmaza egyetlen int-en bell. A mezdefinci s -elrs szintaxisa a struktrkon alapul. Pl. az elbbi #define sorok hrom mez definilsval helyettesthetk: struct { unsigned is_keyword is_static : 1; } flags; : 1; unsigned is_extern : 1; unsigned

142

Ez a flags nev vltozt definilja, amely hrom 1-bites mezt tartalmaz. A kettspontot kvet szm jelenti a mezszlessget bitekben. A mezket unsigned-nak deklarltuk annak hangslyozsra, hogy azok tnylegesen eljel nlkli mennyisgek. Az egyes mezkre flags.is_keyword, flags.is_extern stb. alakkal hivatkozhatunk, ppgy, mint ms struktratagok esetben. A mezk gy viselkednek, mint kis, eljel nlkli egsz szmok, s ppgy rszt vehetnek aritmetikai mveletekben, mint ms egszek. gy a fenti pldkat termszetesebb mdon a kvetkezkppen rhatjuk: flags.is_extern = flags.is_static = 1; amely 1-re lltja; flags.is_extern = flags.is_static = 0; amely kinullzza, s if (flags.is_extern == 0 && flags.is_static == 0) . . . megvizsglja a biteket. amely

A mez nem lpheti t az int hatrt; ha a megadott szlessg erre vezetne, a mez a kvetkez int hatrra fog illeszkedni. A mezknek nem kell felttlenl nvvel rendelkeznik; nv nlkli mezket (csak egy kettspont s a szlessg) hasznlunk kitltsre. A specilis 0 szlessg elrsval a kvetkez int hatrra val illeszkedst knyszerthetjk ki. A mezk hasznlatval kapcsolatban nhny dologra gyelnnk kell! Taln a leglnyegesebb, hogy bizonyos gpeken a mezk hozzrendelse balrl jobbra, ms gpeken jobbrl balra trtnik, ami az eltr hardverfelptst tkrzi. Ebbl kvetkezleg, br a mezk igen hasznosak belsleg definilt adatstruktrk kezelsre, mieltt klsleg definilt adatok sztbontsra hasznlnnk ket, alaposan meg kell fontolni, milyen is lesz, hol kezddik a mezkioszts. Tovbbi megjegyzend megktsek : a mezk eljel nlkliek; csak int-ekben trolhatk (vagy az ezzel egyenrtk unsigned-okban); a mezk nem tmbk; nincsen cmk, gy rjuk az & opertor nem alkalmazhat. 6.8. Unionok A union olyan vltoz, amely (klnbz idpontokban) klnfle tpus s mret objektumokat tartalmazhat oly mdon, hogy a fordt gyel a mretre s illeszkedsre vonatkoz kvetelmnyek teljeslsre. A unionok lehetv teszik, hogy ugyanazon a trterleten klnbzfajta adatokkal dolgozzunk anlkl, hogy a programban gpfgg informcit kellene elhelyeznnk. Pldnkat ismt a fordt szimblumtbljbl vve tegyk fel, hogy llandink int-ek, float-ok vagy karaktermutatk lehetnek. Valamely adott lland rtkt a megfelel tpus vltozban kell trolnunk, ugyanakkor a tblakezels szempontjbl a legknyelmesebb, ha az rtk ugyanannyi trterletet foglal el s ugyanazon a helyen troldik, a tpustl fggetlenl. Ez a union hasznlatnak clja - olyan vltozt ltrehozni, amely megengedett mdon tbb tpus brmelyikt tartalmazhatja. A mezkhz hasonlan a szintaxis a struktrkon alapul. union u_tag { int ival; float fval; char *pval; } uval; Az uval vltoz elg nagy lesz ahhoz, hogy a hrom tpus kzl a legnagyobbat is tartalmazhassa, fggetlenl attl a gptl, amelyen a

143

fordts trtnik - a programkd fggetlen a hardver jellemzitl. E tpusok brmelyike hozzrendelhet uval-hoz, majd kifejezsekben hasznlhat mindaddig, amg a hasznlat kvetkezetes: a visszanyert tpus a legutoljra trolt tpussal kell, hogy megegyezzen. A programoz feladata annak kvetse, hogy ppen mit trolt a unionban; ha valamit adott tpusknt trolunk s ms tpusknt olvassuk ki, akkor az eredmnyek gpfggek. A union tagjaihoz val hozzfrs szintaxisa: unionnv.tag vagy unionmutat->tag csakgy, mint a struktrk esetben. Ha az utype vltoz segtsgvel kvetjk az uval-ban trolt aktulis tpust, akkor az albbihoz hasonl kdot kapunk: if (utype == INT) printf (%d \n, uval.ival); else if (utype == FLOAT) printf (%f \n, uval.fval); else if (utype == STRING) printf (%s \n, uval.pval); else printf (rossz tpus %d az utype-ban\n, utype); Unionok elfordulhatnak struktrkban s tmbkben, ill. viszont. A struktrabeli union (vagy megfordtva) egyik tagjhoz val hozzfrst ler jellsmd azonos az egymsba skatulyzott struktrk jellsmdjval. Pl. a struct { char *name; int flags; int utype; union { int ival; float fval; char *pval ; } uval; } symtab [NSYM]; ltal definilt struktratmbben az ival vltozra a symtab [i].uval.ival alakban, mg a pval karakterlnc els karakterre a *symtab [i].uval.pval alakban hivatkozhatunk. Valjban a union olyan struktra, amelyben a tagok kztti eltols nulla, s amely elg nagy ahhoz, hogy a legszlesebb tagot is tartalmazhassa gy, hogy az illeszkeds a benne elfordul sszes tpus szmra megfeleljen. A struktrkhoz hasonlan a unionokra jelenleg csak kt mvelet megengedett: valamelyik tagjhoz val hozzfrs, ill. a cm ellltsa. A unionokhoz semmit sem lehet hozzrendelni, nem lehet ket fggvnyeknek tadni, s fggvnyek sem adhatnak vissza unionokat. A unionokat megcmz mutatk ugyangy hasznlhatk, mint a struktrk mutati.

144

A 8. fejezetben bemutatsra kerl trterlet-lefoglal szemllteti, hogyan lehet union hasznlatval kiknyszerteni, hogy egy vltoz adott tpus trterlet hatrra illeszkedjen. 6.9. Tpusnvdefincik A C nyelv typedef-nek nevezett szolgltatsnak segtsgvel j adattpus-neveket hozhatunk ltre. Pl. a typedef int LENGTH; deklarci hatsra a LENGTH nv az int szinonimja lesz. A LENGTH tpus deklarciban, tpusmdost szerkezetben stb. pontosan ugyangy hasznlhat, mint az int tpus: LENGTH len, maxlen; LENGTH *lengths []; Hasonlkppen, a typedef char * STRING; deklarci hatsra a STRING a char * , vagyis a karaktermutat szinonimja lesz, amit azutn olyan deklarcikban hasznlhatunk, mint STRING p, lineptr [LINES], alloc (); Figyeljk meg, hogy a typedef-ben deklarlt tpus a vltoznv helyn jelenik meg, nem pedig kzvetlenl a typedef sz utn. A typedef szintaktikusan olyan, mint az extern, static stb. trolsi osztlyok. A nevek hangslyozsa rdekben nagybetket hasznltunk. Bonyolultabb pldaknt typedef-eket ksztnk az ebben a fejezetben korbban bemutatott facsompontok szmra: struct tnode { /*Alapcsompont*/ char *word; /*A szvegre mutat*/ int count; /*Elfordulsok szma*/ struct tnode *left; /*Bal oldali gyermek*/ struct tnode *right; /*Jobb oldali gyermek*/ } TREENODE, *TREEPTR; Ezzel kt j tpuskulcssz, TREENODE (struktra) s TREEPTR (a struktramutatja)jn ltre. Ekkor a talloc rutinbl TREEPTR talloc () { char *alloc (); return ((TREEPTR) alloc (sizeof (TREENODE))); } lesz. Hangslyozzuk, hogy a typedef deklarci semmilyen rtelemben sem hoz ltre j tpust; egyszeren j nevet ad valamilyen, mr ltez tpusnak. Szemantikailag sincs benne semmi j : az ily mdon deklarlt vltozk pontosan ugyanolyan tulajdonsgak, mint azok a vltozk, amelyeknek deklarciit explicit mdon lertuk. Valjban typedef olyan, mint #define, attl eltekintve, hogy mivel a fordt rtelmezi, olyan szveges helyettestsekkel ismeg tud brkzni, amelyek meghaladjk a C makroprocesszor kpessgeit. Pl.: typedef

145

typedef int (*PFI) (); ltrehozza a PFI tpust az int-et visszaad fggvnyt megcmz mutat szmra, amely olyan sszefggsekben hasznlhat, mint PFI strcmp, numcmp, swap; az 5. fejezet rendezprogramjban. A typedef deklarcik hasznlatnak kt f oka van. Az els ok a programok paramterezse a gpfggsgi problmk kivdsre. Ha a typede-feket olyan adattpusokra hasznljuk, amelyek gpfggek, akkor a program thelyezsekor csupn a typedef-eket kell megvltoztatni. Az egyik szoksos eset az, amikor klnfle egsz mennyisgek szmra hasznlunk typedef neveket, majd minden egyes befogad gpre elksztjk a short, int s long vlasztkbl ll megfelel kszletet. A typedef-ek hasznlatnak msik clja a programdokumentls javtsa - a TREEPTR-nek nevezett tpust knnyebb megrteni, mint azt, amelyiket csupn egy bonyolult struktra mutatjaknt deklarltunk. Vgezetl, mindig megvan annak a lehetsge, hogy a jvben a fordt vagy valamelyik msik program, mint pl. a lint fel tudja hasznlni a typedef deklarcikban trolt informcit a program valamilyen kln ellenrzse cljbl. _ 7. fejezet

Bevitel s kivitel A be- s kiviteli (I/O) szolgltatsok nem rszei a C nyelvnek, ezrt ezekre idig nem fordtottunk klnsebb figyelmet. Ktsgtelen azonban, hogy a valdi programok sokkal bonyolultabb mdon llnak kapcsolatban krnye-zetkkel, mint ahogy azt idig bemutattuk. Ebben a fejezetben a szabvnyos (standard) be- s kiviteli knyvtrat ismertetjk; ez a fggvnyeknek olyan kszlete, amelyek a C programok szabvnyos be- s kiviteli rendszert kpezik. E fggvnyek feladata, hogy knyelmes programozsi csatlakozst biztostsanak, ugyanakkor csupn olyan mveleteket valstsanak meg, ame-lyek a legtbb modern opercis rendszerben rendelkezsre llnak. A rutinok - fggetlenl attl, hogy milyen kritikus alkalmazsrl van sz - elg jl mkdnek, a felhasznlk ritkn rezhetik gy, hogy a nagyobb hatkonysg rdekben ms megoldst kell alkalmazniuk. Vgl a rutinok gpfggetlenek abban az rtelemben, hogy kompatibilis formban mkdnek minden olyan _ rendszeren, amelyen a C ltezik, s azok a programok, amelyek a rendszerrel folytatott prbeszdket a szabvnyos knyvtr ltal nyjtott szolgltat-sokra korltozzk, lnyegben vltoztats nlkl vihetk t egyik gprl a msikra. Ezen a helyen nem ksreljk meg a teljes be- s kiviteli knyvtr lerst; sokkal fontosabbnak tartjuk, hogy bemutassuk, hogyan kell az opercis rendszerbeli krnyezetkkel kapcsolatot tart C programokat rni. _ 7.1. Hozzfrs a szabvnyos knyvtrhoz Minden olyan knyvtrbeli forrsllomnynak, amely valamelyik szabvnyos

146

fggvnyre hivatkozik, valahol az llomny elejn tartalmaznia kell az #include < stdio.h > sort.Az stdio.h llomny bizonyos, a be- s kiviteli knyvtr ltal hasznlt makrkat szoksos s vltozkat definil. A < s > knykzrjeleknek a

idzjelek helyetti hasznlata hatsra a fordt az llomnyt abban a katalgusban (directory-ban) fogja keresni, amely a szabvnyos fej (header) infor(a UNIX-ban tipikusan usr include). mcit tartalmaz A program betltsekor szksges lehet tovbb a knyvtr explicit megadsa, a PDP--11 UNIX rendszeren pl. a program fordtst elr parancs cc forrsllomnyook stb. ls ahol ls jelzi a szabvnyos knyvtrbl karakter az el bet, load = betlteni.) trtn betltst. (Az 1

7.2. Szabvnyos be- s kivitel; getchar s putchar A legegyszerbb karakterenknt beviteli mechanizmus az, amikor getchar-ral

olvasunk a szabvnyos bemenetrl (standard inputrl), ltalban a felhasznli terminlrl. getchar() minden hvsa utn a kvetkez bemeneti karaktert adja vissza. tmogatja, a terA legtbb olyan krnyezetben, amely a C-t

minlt egy llomnnyal helyettesthetjk a C konvenci segtsgvel: ha a prog program a getchar-t hasznlja, akkor a prog C infile parancssor hatsra helyett. A bea prog az infile-t fogja olvasni a terminl

menet tkapcsolsa oly mdon trtnik, hogy maga a prog rzketlen a vltoztatsra; kzelebbrl, az Cinfile karakterlnc nem kerl be az argv-beli parancssor-argumentumok parancslnotherprog _ prog kz. Hasonl a helyzet, ha a bemenet

con (pipe) keresztl valamelyik msik programtl rkezik; az

147

parancssor kt programot futtat, mgpedig az otherprog-ot s a progot, s gy intzkedik, hogy a prog szabvnyos bemenete az otherprog szabvnyos kimenetrl jjjn. A getchar az EOF rtket adja vissza, amikor az ltala ppen olvasott, brmifle bemenet vgre rt. A szabvnyos knyvtr az EOF szimbolikus llandt -1-nek definilja (egy #define-nal az stdio.h llomnyban), a vizsg-latokat ennek ellenre EOF-ra s ne -1-re vgezzk, hogy ezltal az adott rtktl fggetlenek maradjunk. Ami a kimenetet kimenetre (standard outputra) terminl. A kiilleti, teszi, a ami putchar a c karaktert szerint szabvnyos szintn a

alaprtelmezs

menet > hasznlatval irnythat llomnyba; ha prog a putchar-t hasznlja, akkor prog > outfile a szabvnyos kimenetet a terminl helyett az outfile-ra rja. A UNIX rend-szerben parancslncot (pipe) is hasznlhatunk: proglanotherprog a prog teszi. szabvnyos kimenett az anotherprog szabvnyos bemenetre

A prog ebben az esetben sem vesz tudomst az tirnytsrl. A printf ltal ltrehozott kimen szveg szintn a szabvnyos kimenetre kerl. A putchar s a printf hvsai keverhetk. Meglepen nagy azoknak a programoknak a szma, amelyek csupn egyetlen bemeneti folyamot olvasnak s csupn egyetlen kimeneti folyamot rnak. Ilyen programok esetben a be- s kivitel getchar, putchar, ill. printf fggvnyekkel trtn megvalstsa teljesen megfelel, s az indulshoz fel-ttlenl elg. Ez klnsen igaz akkor, ha az egyik program kimenetnek a kvetkez program bemenetvel trtn sszekapcsolsa cljbl rendelke-zsre ll az llomny-tirnyts s a parancslnc-mechanizmus. Tekintsk pl. a lower programot, amely a bemenetet kisbetss kpezi le: #include < stdio.h> main() /*A bemenet kisbetss alaktsa*/ int c; while (( c = getchar ()) ! = EOF) putchar(isupper ? tolower : c); Az isupper s tolower fggvnyek valjban az stdio.h-ban definilt makrk. Az isupper makr ellenrzi, hogy az argumentum nagybet-e s nemnullt ad vissza, ha az, ill. nullt, ha nem. A tolower makr a nagybetket kisbetkk alaktja. Fggetlenl attl, hogy az adott gpen ezek a fggvnyek hogyan vannak megvalstva, kvlrl nzve egyformn viselkednek, gy az azokat hasznl programoknak nem kell ismernik a karakterkszletet. Tbb llomny konvertlsakor az llomnyok sszegyjtsre programot hasznlhatunk, mint a UNIX cat segdprogramja: olyan

148

cat file 1, file2. . . _ lower _ output gy nem kell megtanulnunk, hogyan lehet llomnyokat programbl elrni. (A cat-ot e fejezet ksbbi rszben mutatjuk be.) Mellkesen megjegyezzk, hogy a szabvnyos be- s kiviteli knyvtrban a getchar s putchar fggvnyek valjban makrk lehetnek, gy elkerl-het a karakterenknti fggvnyhvs miatti terhels (overhead). A 8. fejezet-ben fogjuk ennek tnyleges megvalstst megmutatni.

7.3. Formtumozott kimenet; printf A kivitel cljbl hasznlt printf s a beolvasst vgz scanf rutin (l. a kvet-kez szakaszt) numerikus mennyisgek karakteres brzolsra s karakteres mennyisgek numerikus brzolsra trtn talaktst, formtumozott sorok ltrehozst s rtelmezst teszi lehetv. A printf fggvnyt az elz fejezetekben ktetlenl hasznltuk, me a teljesebb s pontosabb lers: printf(control, argl, arg2, . . .) A printf az argumentumait konvertlja, formtumozza s a szabvnyos kime-netre nyomtatja a control karakterlnc vezrlete alatt. A vezrl karakterlnc ktfle tpus objektumot tartalmaz: kznsges karaktereket, amelyeket egyszeren a kimeneti folyamra msol s konverzi-specifikcikat, amelyek mindegyike a printf soron kvetkez argumentumnak konvertlst s ki-nyomtatst rja el. Minden konverzi-specifikcit a % karakter vezet be, s konverzis karakter zr le. A % s a konverzis karakter kztt a kvetkezk llhatnak: Mnuszjel,amely az ebbe a mezbe konvertlt argumentum balra igaz-tst rja el. Szmjegyekbl ll karakterlnc,amely a minimlis mezszlessget hatrozza meg. Az talaktott szm legalbb ilyen szles vagy szksg esetn szlesebb mezbe nyomtatdik ki. Ha a konvertlt argumentum kevesebb karakterbl ll, mint a mezszlessg, akkor bal oldalon (vagy, ha a balra igazts jelz szerepel, akkor jobb oldalon) a mez kitltdik, hogy ezltal az elrt mezszlessg meglegyen. A kitlt karakter k-znsges esetben szkz, ill. amennyiben a mezszlessget elnullval adtuk meg, akkor nulla (ez a zrus nem jelent oktlisan rtelmezett mezszlessget). Pont,amely a mezszlessget a rkvetkez szmjegysorozattl v-lasztja el. Szmjegysorozat (a pontossg),amely a lncbl kinyomtatsra kerl I karakterek maximlis szmt vagy float s double esetben a tizedesponttl jobbra kinyomtatand szmjegyek szmt hatrozza meg. Az l (el bet) hosszmdost,amely arra utal,hogy a szban forg adat int helyett long. A konverzis karakterek s jelentsk: d Az argumentum decimlis jellsmdv alakul. Az argumentum eljel nlkli,oktlis szmm konvertldik (elnulla nlkl). x Az argumentum eljel nlkli,hexadecimlis szmm konvertldik (vezet 0x nlkl).

149

u Az argumentum eljel nlkli decimlis jellsmdv alakul. c Az argumentumot egyetlen karakternek tekinti. s Az argumentum karakterlnc; a lncbeli karakterek mindaddig nyomta, tdnak,amg a nullakarakter nem kerl sorra,vagy amg a pontossgi I specifikci ltal elrt szm karakter kirsa meg nem trtnt. e Az argumentumot float-nak vagy double-nak tekinti,s a [-]m.nnnnnnE[-]xx decimlis jellsmdba konvertlja,ahol az nek ; karakterlncnak hosszt a pontossg adja meg. Az alaprtelmezs szerinti pontossg 6. f Az argumentumot float-nak vagy double-nak tekinti,s a [-]mmm.nnnnn decimlis jellsmdba konvertlja,ahol az n-ek karakterlncnak hosszt a pontossg adja meg.Az alaprtelmezs szerinti pontossg 6.Jegyezzk meg,hogy a pontossg nem hatrozza meg az f formtumban kinyomtatott rtkes szmjegyek szmt! %e s %f kzl a rvidebbet hasznlja; az rtktelen nullkat elhagyja. Ha a %-ot kvet karakter nem konverzis karakter,az illet karakter nyomtatsra kerl: gy a % mint %% nyomtathat ki. A legtbb formtumkonverzi jelentse nyilvnval,s a korbbi fejezetekben ezeket megtrgyaltuk.Ez all az egyik kivtel az,hogy a pontossg miknt vonatkozik a karakterlncokra.Az albbi tblzat klnfle specifikciknak a hall, vilg ( 12 karakter) kinyomtatsra gyakorolt hatst mutatja. Minden mez kr kettspontokat helyeztnk, hogy ezzel szemlltessk a mez kiterjedst: 1Os : : hall, vilg : %- I Os : : hall, vilg : %20s : : hall, vilg %a-20s: : hall, vilg %20.I Os : : hall,vil %-20.l Os: : hall,vil %.l Os: : hall,vil : Figyelmeztets: a printf az els argumentumt hasznlja annak eldntsre, hogy mg hny argumentum kvetkezik, s azoknak mi a tpusa. A program sszezavarodik, s rtelmetlen vlaszt kapunk, ha nincs elg argumentum; vagy ha azok nem a megfelel tpusak. 7.1. Gyakorlat. rjunk olyan programot, amely tetszleges bemenetet kpes sszer mdon kinyomtatni! Minimlis feladatknt a nemgrafikus karaktereket (a helyi szoksnak megfelelen) oktlisban vagy hexadecimlis-ban nyomtassa ki, s hajtogassa ssze a hossz sorokat! 7.4. Formtumozott bemenet; scanf A scanf fggvny a printf bemeneti megfelelje, amely az ellenkez irnyban nyjt szmos, a fentiekben lert szolgltatst:

150

scanf(control, argl, arg2, . . .) A scanf karaktereket olvas a szabvnyos bemenetrl, a control-ban meghat-rozott formtum szerint rtelmezi azokat, s az eredmnyeket a tbbi argu-mentumban trolja. A vezrlargumentumot az albbiakban rjuk le; a tbbi argumentum, amelyek mindegyike mutat kell, hogy legyen, azt jelzi, hogy hol kell trolni az talaktott bemenetet. A vezrl karakterlnc ltalban olyan konverzis utastsokat tartal-maz, amelyek feladata a bemeneti jelsorozat kzvetlen rtelmezse. A vezr-l karakterlnc tartalmazhat: Szkzket, tabokat s jsorokat (res karaktereket), amelyeket figyel-men kvl hagy. Kznsges karaktereket (nem %-ot), amelyek vrhatan illeszkednek a bemeneti folyam kvetkez nemres karakterre. Konverzispecifikcikat, amelyek a % karakterbl, a * hozzrende-ls-elnyom karakterbl, egy, a maximlis mezszlessget meghatroz szmbl, valamint egy konverzis karakterbl llnak, ezek kzl a kt kzps (* s a szm) elhagyhat. A konverzispecifikci a kvetkez bemeneti mez talaktst irnytja. Kznsgesen megcmzett az eredmny a megfelel argumentum ltal

vltozba kerl.Ha azonban a * karakter a hozzrendels elnyomst rja el, a vezrls a bemeneti mezt egyszeren tugorja, s nem trtnik rtk-ads.A beolvasott mez definciszeren a nemres karakterek lnca,teht vagy a kvetkez res karakterig tart,vagy addig,amg el nem fogy az esetleg megadott mezszlessg. Ebbl kvetkezleg a scanfsorhatrokon keresztl is olvassa a bemenett, mivel az jsor karakterek res helyek. A konverzis karakter a beolvasott mez rtelmezsre utal; a hozz tartoz argumentumnak mutatnak kell lennie,amint azt a C nyelv rtk szerint hv szemantikja megkvnja. A kvetkez konverzis karakterek megengedettek : d A bemeneten decimlis egsz szmot vr; a megfelel argumentumnak egszre kell mutatnia. A bemeneten oktlis egsz szmot vr (elnullval vagy anlkl); a meg-felel argumentumnak egsz mutatnak kell lennie. x A bemeneten hexadecimlis egsz szmot vr (vezet 0x-szel vagy anlkl); a megfelel argumentumnak egsz mutatnak kell lennie. h A bemeneten short egsz szmot vr; a megfelel argumentum short egszt megcmz mutat kell,hogy legyen. c Egyetlen karaktert vr; a megfelel argumentum karaktermutat kell, hogy legyen; a kvetkez bemeneti karakter a megjellt helyre kerl. Az res karakterek szoksos tugrsa ebben az esetben letiltdik; a kvetkez nemres karakter beolvasshoz hasznljunk %cls-t. s Karakterlncot vr; a megfelel argumentum karaktermutat; olyan karaktertmbre mutat,amely elg nagy ahhoz,hogy befogadja a karakterlncot s a lezr \0-t. f Lebegpontos szmot vr; a megfelel argumentum float-ot megcmz mutat kell,hogy legyen.Az e konverzis karakter az f szinonimja. A float-ok bemeneti formtuma: eljel (elhagyhat),szmokbl ll

151

lnc,amely tizedespontot s egy (elhagyhat) kitevmezt tartalmazhat,amely utbbi egy E-bl vagy e-bl s az azt kvet,esetleg eljeles egsz szmbl ll. A d,o s x konverzis karaktereket az l (el bet) elzheti meg,amely arra utal,hogy az argumentumlistban long-ot s nem int-et megcmz mutat jelenik meg.Az e vagy f konverzis karaktereket ugyancsak megelzheti az 1, ebben az esetben azt jelezve,hogy az argumentumlista double-ra s nem float-ra hivatkoz mutatt tartalmaz.Pldul az int i; float x; char name[50]; scanf(%d %af%s, &i, &x, name); hvs a 25 54.32E 1 Tltompson bemenet esetn az i-hez a 25 rtket rendeli hozz, az x-hez az 5.432 rtket s a name-hez a \0-val rendesen lezrt Thompson karakterlncot. A hrom bemeneti karakterlncot tetszleges jsorral lehet egymstl elvlasztani. Az szm szkzzel, tabbal s

int i; float x; char name[50]; scanf(%,2d %f%*d %2s, &i, &x, name) hvs az 56789 0123 45a72 bemenettel 56-ot rendel i-hez, 789.0-t az x-hez, tugorja a 0123-at s a 45 karakterlncot a name-be teszi. Brmelyik bemeneti rutin kvetkez hvsa az a betnl trtn keresssel fog indulni. E kt pldban a name mutat, s nem elzheti meg az & szimblum. Msik pldaknt a 4. fejezetben ismertetett elemi kalkultorprogramot most gy rjuk t, hogy a scanf vgezze a bemeneti konverzit: #include < stdio.h> main() / *Elemi kalkultorprogram*/ double sum, v; sum = 0; while (scanf(%lf, &v) != EOF) printf(\t%.2f\n, sum += v); A scanf akkor fejezi be mkdst, amikor kimertette a vezrl karakter-lnct, vagy amikor valamelyik bemenet nem illeszkedik a vezrlsi specifi-kcira. A scanf visszatrsi rtke a sikeresen illesztett s hozzrendelt be-meneti ttelek szma. Ebbl

152

meghatrozhat, hogy a scanf hny bemeneti ttelt tallt. Allomny vge esetn a visszaadott rtk EOF; gyeljnk arra, hogy ez nulltl eltr rtk, amely azt jelenti, hogy a kvetkez bemeneti karakter nem illeszkedik a vezrl karakterlnc els specifikcijra! A scanf kvetkez hvsakor a keress kzvetlenl az utoljra visszaadott karakter utn folytatdik. Mg egy utols figyelmeztets: a scanf argumentumainak mutatknak kell lennik! A leggyakoribb hiba, amikor valaki azt rja, hogy scanf(%d, n); ahelyett, hogy scanf(%d, &n) _ Ft rna.

7.5. Formtumkonverzi a tron bell A scanf s printf fggvnyekkel rokon az sscanf s sprintf, amelyek ugyan-ezeket a konverzikat vgzik, de llomny helyett karakterlncon dolgoznak. Az ltalnos formtum: sprintf(string, control, argl, arg2, . . .) sscanf(string, control, argl, arg2, . . .) Az elzekhez hasonlan az sprintf a control szerint formtumozza az argl, arg2 stb.-beli kimenet argumentumokat, az eredmnyt azonban a szabvnyos

helyett a string-be teszi. A string-nek termszetesen elg nagynak kell lennie ahhoz, hogy befogadja az eredmnyt. Ha pl. string karaktertmb s n egsz, akkor sprintf(name, temp%d, n); a name-ben ltrehoz egy tempNNN alak karakterlncot, ahol NNN az n rtke. Az sscanf az control-ban ellenttes irny konverzikat hozza ltre a

megadott formtum szerint vgighalad a karakterlncon, s a kapott eredmnyeket az mutatkargl, arg2 stb.-ben helyezi el. Ezen argumentumoknak

nak kell lennik. A sscanf(name, temp%d, &n); hvs az n-et annak a szmjegyekbl ll karakterlncnak az rtkre lltja be, amely a name-ban a temp-et kveti. 7.2. Gyakorlat. rjuk meg jra a 4. fejezetben ltott kalkultorprogra-mot gy, hogy a bemenetre s a szmkonverzira a scanf s/vagy sscanf fgg-vnyeket alkalmazzuk!

153

7.6. llomny-hozzfrs Az idig megrt programok mindegyike a szabvnyos bemenetet olvasta s a szabvnyos kimenetre rt, amelyekrl mindeddig feltteleztk, hogy valami-lyen varzslatos mdon a helyi opercis rendszer elre definilta ket a sz-munkra. A be- s kivitellel val ismerkedsnk kvetkez lpseknt olyan programot runk, amellyel programhoz nem rendelt llomnyhoz frhetnk hozz. Az ilyen mveletek szksgessgt vilgosan bizonyt program a cat, amely megnevezett szabvllomnyok halmazt gyjti ki (konkatenlja) a

nyos kimenetre. A cat feladata llomnyoknak a terminlra trtn kinyomtatsa, valamint programok frni. Pl. a cat x.c y.c parancs az x.c s y.c llomnyok tartalmt a szabvnyos kimenetre nyom-tatja. Krds, hogyan rjk el, hogy a megnevezett llomnyok beolvassra kerljenek - azaz, hogyan kthetjk a felhasznl ltal kigondolt kls ne-veket azokhoz az utastsokhoz, amelyek tnylegesen elolvassk az adatokat. A szablyok egyszerek. Mieltt egy llomnyt olvasni vagy rni lehetne, az fopen szabvnyos knyvtri fggvnnyel meg kell nyitni. Az fopen vesz egy kls nevet (mint x.c vagy y.c), bizonyos nyilvntartst vgez, s prbeszdet folytat az opercis rendszerrel (aminek rszleteivel nem kell trdnnk), s olyan bels nevet ad vissza, amelyet az llomny ezutn kvetkez olvassai, ill. rsai sorn hasznlnunk kell. E bels nv valjban mutat, amelyet llomnymutatnak neveznk, s amely egy, az llomnyrl klnbz informcikat tartalmaz struktrra mutat. Itt tallhat pl. a puffer cme, a pillanatnyi pufferbeli karakter-pozci, annak jelzse, hogy az llomny ppen olvass vagy rs alatt ll stb. A felhasznlknak a rszleteket nem kell ismernik, mivel az stdio.htl nyert szabvnyos be- s kiviteli defincik egyik rsze a FILE-nak nevezett struktradefinci. deklarcira nzve plda a FILE *fopen(), *fp; Eszerint fp FILE-t megcmz mutat, s fopen szintn ilyen mutatval tr vissza. Figyeljk meg, hogy FILE, csakgy, mint int, tpusnv, Az llomnymutat szmra szksges egyetlen ltalnos cl bemeneti informcigyjts azon

szmra, amelyek maguk nem kpesek llomnyokhoz nv szerint hozz-

154

nem pedig struktracmke; typedef-knt valstottk meg. (Annak rszleteit, hogy mindez miknt mkdik a UNIX opercis rendszerben, a 8. fejezetben ismertetjk.) Az fopen tnyleges hvsa a programon bell gy fest: fp = fopen(name, mode); Az fopen els argumentuma az llomny neve, amely egy karakterlnc. A m-sodik argumentum, amely szintn karakterlnc, a md, amely azt jelzi, hogy a felhasznl hogyan akarja hasznlni az llomnyt. A megengedett mdok az olvass (r: read), az rs (w: write) s a hozzfggeszts (a: append). Ha rsra vagy hozzfggesztsre nem ltez llomnyt nyitunk meg, akkor az illet llomny (ha lehet) ltrejn. Ltez llomny rsra trtn megnyitsnak hatsra annak korbbi tartalma elvsz. Hibt jelent, ha nem-ltez llomnyt olvasni akarunk. Ms hibaokok is elfordulhatnak (pl. ha olyan llomnyt prblunk meg olvasni, amelyre nincs engedlynk). Brmi-lyen hiba esetn az fopen a NULL mutatrtkkel tr vissza (amelynek defi-ncija a knyelem kedvrt szintn stdio.h-ban van). A kvetkezkben azt kell tudnunk, hogyan olvashatjuk a mr megnyitott llomnyokat. Tbb lehetsg van, amelyek kzl a getc s putc csupn a legegyszerbb. getc az llomny soronkvetkez karaktervel tr vissza; llomnymutatval kell megadnunk, hogy melyik llomnyrl van sz. Igy c = getc(fp) az fp ltal hivatkozott llomnybl a kvetkez karaktert c-be helyezi, ill EOF kerl c-be, ha elrtk az llomny vgt. A putc a getc inverze : putc(c, fp) a c karaktert az fp llomnyba helyezi s c-t adja vissza. A getchar s putchar fggvnyekhez hasonlan a getc s putc is lehet fggvny helyett makr. Hrom llomny minden program indtsakor automatikusan megny-lik, s a rendszer llomnymutatkat is rendelkezsre bocst szmukra. Ezek az llomnyok : a szabvnyos bemenet, a szabvnyos kimenet s a szab-vnyos hibakimenet; az ezeknek megfelel mutatk neve: stdin, stdout s stderr. Kznsges esetben ezek mindegyike a terminlhoz van rendelve, azonban az stdin s stderr mutatkat a 7.2. szakaszban lert mdon llom-nyokba vagy parancslncokba lehet tirnytani. A getchar s a putchar az albbi mdon definilhat a getc, a putc, az stdin s az stdout segtsgvel: #define getchar() getc(stdin) #define putchar putc(c, stdout) llomnyok formtumozott beolvassra vagy kiratsra az fscanf s fprintf fggvnyeket hasznlhatjuk. Ezek azonosak a scanf s printf fggvnyekkel, eltekintve attl, hogy az els argumentum llomnymutat, amely az olva-sand vagy rand llomnyt hatrozza meg; a vezrl karakterlnc a mso-dik argumentum. E bevezets utn mr abban a helyzetben vagyunk, hogy megrhatjuk az llomnyokat konkatenl cat programot. Az alapfelpts azonos

155

azzal, ami mr sok programban knyelmesnek bizonyult: ha vannak parancssor-ar-gumentumok, akkor azok feldolgozsa sorrendben trtnik. Ha nincsenek argumentumok, akkor a szabvnyos bemenetet dolgozzuk fel. Ily mdon a program akr nllan, akr valamely nagyobb feldolgozs rszeknt hasz-nlhat. #include < stdio.h> main(argc, argv) konkatenlsa*/ int argc; char * argv [] ; FILE *fp, * fopen(); if (argc == 1 ) /* Nincs arg., a szabvnyos bemenetet msolja*/ else filecopy(stdin); while (--argc _ 0) if((fp = fopen(*++argv, r)) == NULL){ printf(cat: nem nyithat meg %cs\n, *argv); break; } else _ filecopy(fp); fclose(fp); filecopy(fp) /*llomny msolsa a szabvnyos kimenetre*/ FILE *fp; int c; while ((c = getc(fp)) != EOF) putc(c, stdout); Az stdin, ill. stdout llomnymutatk a be- s kiviteli knyvtrban szab-vnyos bemenetknt, ill. szabvnyos kimenetknt elredefiniltak; min-dentt hasznlhatk, ahol FILE tpus objektumokat hasznlni lehet. Ezek azonban llandk s nem vltozk, teht ne prbljunk semmit sem hozzjuk rendelni ! Az fclose fggvny az fopen inverze: megszaktja az llomnymutat s a kls nv kztt az fopen ltal ltrehozott kapcsolatot, s gy az llomny-mutat egy msik llomny szmra szabadul fel. Mivel a legtbb opercis rendszerben az egyidejleg megnyitott llomnyok szma korltozott, cl-szer azokat felszabadtani, ha mr nincs rjuk szksg, amint ezt a cat-ban is tettk. Az fclose kimeneti llomnyra val alkalmazsnak msik oka is van: rti azt a puffert, amelyben a putc a kimenetet gyjti. (A program normlis befejezdsekor az fclose automatikusan meghvdik minden megnyitott llomnyra. ) /* cat: llomnyok

7.7. Hibakezels; stderr s exit A hibknak az a fajta kezelse, amit a cat-ban hasznltunk, nem idelis. A baj az, hogy ha az llomnyok egyike valamely oknl fogva hozzfrhetetlen, a hibajelzs a konkatenlt kimenet vgre nyomtatdik. Ez elfogadhat, ha a kimenet a terminlra irnyul, azonban rossz, ha egy llomnyba vagy pa-rancslncon keresztl egy msik programba megy. A jobb hibakezels rdekben az stdin s stdout llomnnyal azonos mdon a programhoz egy msodik kimeneti llomny, az stderr is hozz van rendelve. Ha egyltaln lehetsges, az stderr-re rt kimenet mg

156

akkor is meg-jelenik a felhasznli terminlon, amikor a szabvnyos kimenetet tirny-tottk. Mdostsuk a cat programot gy, hogy a hibazeneteket a szabvnyos hiballomnyra rja ! #include < stdio.h> main(argc, argv) konkatenlsa* / int argc; char * argv[ ]; FILE *fp, *fopen(); if (argc ==1 ) /*Nincs arg., a szabvnyos bemenetet msolja* / filecopy(stdin); else while (--argc _ 0) if ((fp = fopen(*++argv, r )) == NULL)_ fprintf(stderr, cat: nem nyithat meg %s\n, argv) ; exit( 1 ); } else { filecopy(fp); fclose(fp); exit(0); } A program ktflekppen jelzi a hibkat. Az fprintf ltal ellltott diagnosz-tikai kimenet az stderr-re megy, teht a felhasznl terminljra kerl, s nem tnik el egy parancslncon keresztl vagy valamelyik kimeneti llo-mnyban. A program az exit szabvnyos knyvtri fggvnyt is hasznlja. Az exit meghvsa a program befejezdst eredmnyezi. Az exit argumentum br-milyen, az exit-et hv folyamat rendelkezsre ll, gy a program sikeres vagy sikertelen lefutst egy msik program oly mdon ellenrizheti, hogy ezt a programot mint rszfolyamatot hasznlja. Megllapods szerint a 0 visz-szatrsi rtk azt jelzi, hogy minden rendben ment, mg a klnfle nem-nulla rtkek a normlistl eltr llapotokat jelzik. Az exit minden megnyitott kimeneti llomnyra meghvja az fclose-t az sszes pufferelt kimenet kirtse rdekben, majd meghvja az -exit nev rutint, amelynek hatsra a programfuts mindenfle pufferrts nlkl azonnal vgetr. Az exit szksg esetn termszetesen kzvetlenl is hvhat. /* cat: llomnyok

7.8. Szvegsorok beolvassa s kivitele A szabvnyos knyvtrban rendelkezsre ll az fgets rutin, amely meglehetsen hasonlt a knyvben vgig hasznlt getline fggvnyhez. Az fgets(line, MAXLINE, fp) hvs az fp llomnybl a line karaktertmbbe beolvassa a kvetkez beme-neti sort (az jsort is belertve); legfeljebb MAXLINE -1 sort fog olvasni. A kapott tmb \ 0-val zrul. Norml esetben az fgets a line-t adja vissza, llo-mny vgn pedig NULL-t. (A getline fggvnynk a sorhosszat, ill. llomny vge esetn a nullt adja vissza.) Kivitelkor az fputs fggvny karaktersorozatot (amely nem kell, hogy jsort tartalmazzon) r az llomnyra:

157

fputs(line, fp) Annak rzkeltetsre, hogy az olyan fggvnyek krl, mint fgets s fputs nincs semmi varzslatos, a szabvnyos be- s kiviteli knyvtrbl kzvetlenl ide msoltuk e fggvnyek programkdjt:

#include < stdio.h> char * fgets(s, n, iop) / * Legfeljebb n karakter olvassa ioprl* / char * s; int n; register FILE *iop; register int c; register char * cs; cs = s; while ( --n > 0 && (c = getc(iop)) != EOF) if ((*cs++ = c) =- \n) break; *cs = \0; return((c == EOF && cs == s) ? NULL : s); fputs(s, iop) j *Az s karakterlncot az iop llomnyra rja*/ register char *s; register FILE *iop; register int c; while (c = *s++) putc(c, iop);

7.3. Cyakorlat. rjunk olyan programot, amelyik sszehasonlt kt llo-mnyt, s kirja az els olyan sort s karakterpozcit, ahol az llomnyok el-trnek egymstl! 7.4. Gyakorlat. Mdostsuk az 5. fejezet mintakeres programjt oly mdon, hogy a bemenett argumentumokknt megnevezett llomnyok hal-mazbl vegye, vagy ha ilyenek nincsenek, akkor a szabvnyos bemenetrl! Ki kell-e ratni az llomny nevt, ha a program egymsra illeszked sorokat tall? 7.5. Cy,akurlat. rjunk olyan programot, amely tbb llomnyt nyomtat ki, minden ijabb llomnyt j oldalon, cm kirsval kezd, s az oldalakat llomnyonknt folyamatosan szmozza!

7.9. Nhny tovbbi fggvny A szabvnyos knyvtr szmos fggvnyt bocst rendelkezsnkre, amelyek kzl nhny klnsen hasznos. Mr emltettk az strlen, strcpy, strcat s strcmp harakterlnc-kezel fggvnyeket. me nhny tovbbi fggvny.

Karakterosztly-vizsglat s -talakts Tbb makro vgez karaktervizsglatot s talaktst isalpha nemnulla,ha c alfabetikus,0,ha nem.

158

isupper islower isdigit isspace toupper tolower

nemnulla,ha c nemnulla,ha c nemnulla,ha c nemnulla,ha c c talaktsa c talaktsa

nagybet,0,ha nem. kisbet,0,ha nem. szmjegy,0,ha nem. szkz,tab vagy jsor,0,ha nem nagybetss. kisbetss.

Az ungetch fggvny A szabvnyos knyvtrban megtalljuk a 4. fejezetben ltalunk megrt ungetch fggvny egy meglehetsen szktett vltozatt; ennek neve ungetc. Az ungetc(c, fp); a c karaktert az fp llomnyba helyezi vissza. llomnyonknt csak egy karakternyi visszatols megengedett. Az ungetc minden olyan bemeneti fgg-vnnyel s makrval egytt hasznlhat, mint a scanf, getc vagy a getchar. Rendszerhvs A system(s) fggvny hajtja

az

karakterlncban

tartalmazott

parancsot

vgre, majd visszatr az adott program vgrehajtshoz. Az s tartalma ersen fgg a helyi opercis rendszertl.Trivilis pldaknt a UNIX-ban a system(date); sor hatsra lefut a date nev kinyomtatja a dtumot s a na-pon belli idpontot. program; amely

Trkezels A calloc fggvny igen hasonlt a korbbi fejezetekben hasznlt alloc fgg-vnyre. calloc(n, sizeof(objektum)) egy mutatt szolgltat, amely olyan helyre mutat, ahol elegend hely van n darab megadott mret objektum szmra, ill. a NULL rtket adja vissza, ha a krs nem teljesthet. A trterlet kezdeti nagysga nulla. A mutat a szban forg objektum tpusnak megfelel helyre mutat, azonban tpusmdost szerkezettel a megfelel tpusv kell alaktani, pl.: char * calloc() ; int *ip; ip = (int *) calloc(n, sizeof(int));

A cfree(p) felszabadtja a p ltal megcmzett helyet, ahol p-t eredetileg calloc valamelyik hvsval nyertk. A helyfelszabadts sorrendjre nincs megkts, azonban vgzetes hiba, ha olyasvalamit szabadtunk fel, amit nem a calloc hvsval nyertnk. A 8. fejezetben bemutatjuk a calloc-hoz hasonl trterlet-foglal fgg-vny megvalstst, amelyben a lefoglalt blokkok tetszleges sorrendben szabadthatk fel.

159

164

160

_ 8. fejezet

Csatlakozs a lJNIX opercis rendszerhez

E fejezet anyaga a C programok s a UNIX opercis rendszer kztti kapcsolattal foglalkozik. Mivel a legtbb C programoz UNIX rendszer alatt dolgozik, ezek az ismeretek az olvask tbbsge szmra hasznosak lesznek. St, mg ha az olvas a C nyelvet ms gpen is hasznlja, e pldk tanulm-nyozsa rvn mlyebb betekintst nyerhet magba a C programozsba is. A fejezet hrom f tmakrre oszlik: bevitel/kivitel, llomnykezels s trterlet-foglals. Az els kt rsz felttelezi a UNIX kls meg_elensnek legalbb nmi ismerett. A 7. fejezet olyan rendszer-hatrfellettel foglalkozott, amely szmos opercis rendszerben egyforma. Brmelyik konkrt rendszerben a szabv-nyos knyvtr rutinjait a befogad rendszerben rendelkezsre ll be- s kivi-teli szolgltatsok figyelembevtelvel kell megrni. A kvetkez nhny sza-kaszban a UNIX opercis rendszer bes kiviteli rendszernek alapvet bel-psi pontjait ismertetjk, s azt szemlltetjk, miknt lehet ezek segtsgvel a szabvnyos knyvtr egyes rszeit megvalstani.

8.1. Allomnylerk A UNIX opercis rendszerben az sszes be- s kivitel llomnyok rsval s olvassval valsul meg, mivel az sszes perifria, mg a felhasznl terminl-ja is egy-egy llomnyknt jelenik meg. Ez azt jelenti, hogy egyetlen homo-gn csatolprogram kezeli a program s a perifrik kztt az sszes kapcso-latot. A legltalnosabb esetben egy llomny rsa vagy olvassa eltt rtestennk kell a rendszert errl a szndkunkrl. Ezt a folyamatot az llomny megnyitsnak nevezzk. Ha rni akarunk egy llomnyba, akkor szksg lehet az llomny minderre van-e ltrehozsra is. A rendszer ellenrzi, hogy

jogunk (Ltezik-e az llomny? Van-e hozzfrsi engedlynk?), s ha minden rendben van, akkor a programhoz egy llomnyleirnak nevezett kis egsz szmmal tr vissz. Minden esetben, amikor az llomnyon bevagy kiviteli mveletet akarunk vgezni, az llomny azonostsa cljbl annak

161

neve helyett az llomnylert hasznljuk (Ez nagyjbl hasonlt a READ(5, . . .) s WRITE(6, . . .) hasznlatra a FORTRAN-ban.) A megnyi165

162

8.3. Open, creat, close, unlink Az alaprtelmezs szerinti szabvnyos bemeneti, kimeneti s hibakimeneti llomnyon kvl az sszes llomnyt explicit mdon meg kell nyitnunk, ha azokat rni vagy olvasni akarjuk. Ebbl a clbl kt rendszerbelpsi pont ll rendelkezsre: az open s a creat (vigyzat, nem create!). Az open lnyegben ugyanolyan, mint a 7. fejezetben trgyalt fopen, el-tekintve attl, hogy nem llomnymutatt ad vissza, hanem llomnylert, ami egyszeren egy int. int fd ; fd = open(name, rwmode); Az fopen-hez hasonlan a name argumentum a kls llomnynvnek megfe-lel karakterlnc. Az elrs mdja azonban eltr: az rwmode rtke olva-sskor 0, rskor 1, s egyidej rsi-olvassi hozzfrs esetn 2. Hiba el-fordulsakor az open -1-et ad vissza, egybknt a visszatrsi rtk az rv-nyes llomnyler. Hibhoz vezet, ha nem ltez llomnyt prblunk meg-nyitni. A creat belpsi pont j llomnyok ltrehozsra vagy rgiek fellrsra szolgl : fd = creat(name, pmode); llomnylert ad vissza, ha ltre tudta hozni a name nev llomnyt, s -1-et, ha nem. Ha az llomny mr ltezik, a creat nulla hosszsgra vgja le, nem jelent teht hibt mr ltez llomny creat-tel trtn ltre-hozsa. Ha az llomny vadonatj, a creat azt a pmode argumentumban meg-adott vdelmi mddal hozza ltre. A UNIX rendszerben minden llomny-hoz kilenc bitbl ll vdelmi informci trsul. Ezek a bitek az llomny tulajdonosra, a tulajdonos csoportjra, valamint a msokra vonatkoz olva-ssi, rsi s vgrehajtsi engedlyeket szablyozzk. Az engedlyeket gy legknyelmesebben egy hromjegy oktlis szmmal adhatjuk meg. PI. 0755 olvassi-rsi-vgrehajtsi engedlyt ad a tulajdonosnak, s olvassi-vgrehaj-tsi engedlyt a csoport tagjainak s mindenki msnak. Szemlltets cljbl kzljk a UNIX cp nev segdprogramjnak egyszerstett vltozatt, amely egy llomnyt egy msikba msol. (A f egysze-rsts az, hogy az itt kzlt vltozat csak egyetlen llomnyt msol s nem teszi lehetv, hogy a msodik argumentum katalgus (directory) legyen.)

168

163

#define NULL 0 #define BUFSIZE 512 #define PMODE 0644 %* RW a tulajdonosnak, R a csQportnak s msoknak* / main(argc, argv)/ * cp: fl msolsa f2-be*/ int argc; char * argv [ ] ; int fl, f2, n; char buf [BUFSIZE]; if (argc ! = 3 ) error(Hasznlat: cp honnan open(argv[ 1 ], 0)) = -1 ) error(cp: nem nyithat meg creat(argv[2], PMODE)) = -1)

hov, %s,

NULL); argv[1

if ]);

((fl if((f2

= =

error(cp: nem hozhat ltre %s, argv[2]); while ((n = read(fl, buf, BUFSIZE )) > 0) if (write(f2, buf, n) != n) error(cp: rshiba, NULL); exit(0); error(sl, s2) / *A hibazenetet kirja s lell* / char * s I, * s2 ; printf(s 1, s2); printf(\n); exit(1 ); A programok ltal egyidejleg nyitva tarthat llomnyok szma korltozott (tipikusan 15-25). Ennek megfelelen minden olyan programot, amelynek sok llomnyt kell feldolgoznia, gy kell elkszteni, hogy kpes legyen az llomnylerk jbli hasznlatra. A close rutin megszaktja az llomny-ler s a megnyitott llomny kztti kapcsolatot s felszabadtja az llo-mnylert, gy azt a ksbbiekben ms llomny hasznlhatja. A program exit hatsra trtn befejezse s a fprogrambl val visszatrs az sszes megnyitott llomnyt lezrja. Az unlink(filename) fggvny a filename nev llomnyt trli az llomnyrendszerbl. 8.I. Gyakorlat. Irjuk t a 7. fejezetben ltott cat programot gy, hogy a read, write, open s close rutinokat hasznljuk azok szabvnyos knyvtr-beli megfeleli helyett! Vgezznk ksrleteket a kt vltozat egymshoz vi-szonytott sebessgnek meghatrozsra! 0 169

164

R.4. Vletlen hozzfrs; seek s Iseek llomnyok be- s kivitele ltalban soros: minden read s write az llo-mnynak azon a pozcijn trtnik, amely kzvetlenl a megelz be- vagy kivitel llomnybeli pozcijt kveti. Szksg esetn azonban az llomny tetszleges sorrendben olvashat vagy rhat. Az lseek rendszerhvs lehetv teszi, hogy tnyleges olvass vagy rs nlkl mozoghassunk az llomnyban: lseek(fd, offset, origin); hatsra az fd lerj llomnyban az aktulis pozci az offset pozcira mozdul, amelyet az origin ltal meghatrozott helyhez kpest relatven rtel-meznk. Az ezt kvet olvass vagy rs ezen az j pozcin fog kezddni. Az offset long tpus: az fd s az origin int tpusak. Az origin 0, 1 vagy 2 lehet, jelezve, hogy az offset-et az llomny elejtl, a pillanatnyi pozcitl, vagy az llomny vgtl kell szmtani. Ha pl. az llomnyhoz valamit hozz akarunk fggeszteni, rs eltt keressk meg az llomny vgt : lseek(fd, OL, 2); Ha vissza akarunk trni az llomny elejre (visszatekercsels) lseek(fd, OL, 0); Figyeljk meg a OL argumentumot, ezt (long)0-nak is rhatnnk. Az lseek hasznlatval lehetsgnk van arra, hogy az llomnyokat lassbb hozzfrs rn nagy tmbkhz hasonlan kezeljk. Az albbi egyszer fggvny pl. az llomny tetszleges pontjrl tetszleges szm byte-ot olvas be: get(fd, pos, buf, n) / * n byte olvassa a pos pozcirl* / int fd, n; long pos; char * buf; lseek(fd, pos, 0); /*Elmegy pos-ra*/ return(read(fd, buf, n)); A UNlX rendszer 7-est megelz vltozataiban a be- s kiviteli rendszer alap-vet belpsi pontjnak neve: seek. A seek s az lseek azonosak, attl elte-kintve, hogy az elbbinek az offset argumentuma nem long, hanem int. Ennek megfelelen, mivel a PDP I 1 int-ek 16 bitesek, a seek-nek megadhat offset fels korltja 65535; ezrt a 3, 4, 5 origin rtkek hatsra a seek a megadott offset rtket 512-vel la fizikai blokkban tallhat byte-ok szm-val) megszorozza, majd az origin-t gy rtelmezi, mintha az adott sorrendben 0, 1 vagy 2 lenne. lly mdon, ha egy nagy llomny tetszleges pontjra akarunk lpni, akkor kt seek-re van szksgnk: az elsvel a blokkot vlaszt-juk ki, a msodikkal pedig, amelyben az origin rtke I, a blokkon bell a k-vnt byte-ra mozdulunk. 8. ?. C__u_urlat. Vilgos, hogy az lseek a seek felhasznlsval megrhat s viszont. Irjuk meg mindkettt a msik felhasznlsval! 0

165

8.5. Plda; az fopen s a getc megvalstsa Prbljuk meg egysgbe foglalva szemlltetni a mondottakat az fopen s getc szabvnyos knyvtri rutinok egyik megvalstsnak bemutatsval. Emlkezznk lloarra, hogy a szabvnyos knyvtr llomny-ait nem

_ mnylerk, hanem llomnymutatk jellemzik. Ez utbbiak olyan struktrra mutatnak, amely az llomnyra vonatkoz klnbz informcikat tartalmaz: egy puffert megcmz mutatt, ami lehetv teszi az informci nagy darabokban trtn beolvasst; a pufferben maradt karakterek darabszmt; a kvetkez pufferbeli karakterpozcit megcmz mutatt; nhny _ jelzt (flag-et), amelyek pl. az olvass/rs mdot rjk le; s vgl az llomnylert. Az llomnyt ler adatstruktra az stdio.h llomnyban tallhat, amelyet (#include-dal) minden olyan forrsllomnyba be kell iktatni, amely a szabvnyos knyvtr valamelyik rutinjt hasznlja. A knyvtrbeli fggv_ nyek ugyancsak tartalmazzk. Az stdio.h-bl vett albbi kivonatban azok a nevek, amelyeket csak a knyvtrbeli fggvnyek hasznlhatnak, alhzssa1 kezddnek, gy kisebb annak a valsznsge, hogy valamelyik felhasznli programbeli nvvel sszetkzsbe kerljenek. #define -BUFSIZE 512 #define NFILE 20 /*Kezelhet llomnyok szma* ! typedef struct -iobuf( char * -ptr; / * Kvetkez karakterpozci*/ int cnt; /* Megmaradt karakterek szma* / char * - base; /* A puffer cme* / int flag; /* Az llomnyelrs mdja* / int fd; / * llomnyler* / }FILE; extern FILE iob[ NFILE]; #define stdin (& iob[0]) #define stdout (& iob[ 1 ]) #define stderr (& iob[2]) #define READ 01 j *llomnymegnyits olvassra*/ #define -WRITE 02 /* llomnymegnyits rsra */ #define UNBUF 04 /*Az llomny puffereletlen* j #define -BIGBUF 010 /* Nagy pufferlefoglals* / #define EOF 020 /* EOF fordult el ebben az llomnyban* / #define ERR 040 /*Hiba fordult el ebben az llomnyban*/ #define NULL 0 #define EOF ( 1) #define getc(p) ( = -(p) - 7 cnt > = 0\ ? *(p) - > ptr++ & 0377: fillbuf(p)) #define getchar() getc(stdin) #define putc(x,p) (--(p) = _ cnt > = 0\

166

? *(p) - _ =ptr++ = (x) : -flushbuf((xj,p)) #define putehar(x) putc(x,stdout)

167

A getc makr norml esetben egyszeren dekrementlja a darabszmot, el-relpteti a mutatt, s visszaadja a karaktert. (A hossz #define-okat ford-tott \ trtvonallal lehet folytatni.) Ha a darabszm negatvv vlik, a getc meghvja a __fillbuf fggvnyt, amivel jratlti a puffert, jrainicializlja a struktra tartalmt, s egy karaktert ad vissza. A fggvnyek rendelkezhet-nek gpfggetlen csatlakozfellettel, akkor is, ha maguk gpfgg konstruk-cikat tartalmaznak: a getc 0377-tel maszkolja a karaktert, amely fellbrlja a PDP =11 ltal vgrehajtott eljel-kiterjesztst, s biztostja, hogy minden karakter pozitv legyen. Br nem kvnunk rszletekbe menni, mgis beiktattuk a putc definci-jt annak bemutatsra, hogy az lnyegben ugyangy mkdik, mint a getc, azaz amikor a puffere megtelt, meghvja a _flushbuf fggvnyt. Ezek utn megrhatjuk az fopen fggvnyt. Az fopen legnagyobb rsze azzal foglalkozik, hogy megnyitja az llomnyt, a megfelel helyre pozicio-nlja, s gy lltja be a jelzbiteket, hogy azok a helyes llapotot mutassk. Az fopen pufferterletet nem foglal le: ezt az llomny els olvassakor a _fillbuf vgzi.

172

168

#include stdio.h #define PMODE 0644 _ * R/W a tulajdonosnak; R msoknak* I FILE *fopen(name, mode) /*Megnyitja az llomnyt, az llomnymutatt adja vissza* / register char *name, *mode; register int fd ; register FILE *fp; if(*mode != r && *mode != w && *mode != a) f fprintf(stderr, tiltott md %s a %s megnyitsakor \n, mode, name); exit(1 ); for (fp = _iob; fp C -iob + -NFILE; fp++) if ((fp- _ -flag & (READ _ -WRITE)) == 0) break; / * Szabad terletet tallt* / if (fp _ = -iob + -NFILE) /*Nincs szabad hely*/ return(NULL); if (*mode =- w) /*Allomnyhozzfrs* I fd = creat(name, PMODE); else if (*mode == a) { if ((fd = open(name,1 )) =- -1 ) fd = creat(name, PMODE); lseek(fd, OL, 2); } else fd = open(name, 0); if (fd = -1 ) / * Nem tudta a nevet elrni* I return(NULL); fp- _ -fd = fd; fp- > -cnt = 0; fp- > -base = NULL; fp- > -flag & = ^(-READ _ -WRITE); fp- _ -flag _ = (*mode == r ) ? -READ : -WRITE; return(fp); A -fillbuf fggvny jval bonyolultabb. A bonyolultsg f oka, hogy -fillbuf akkor is megksrli az llomny-hozzfrs engedlyezst, ha eset-leg a be- s kivitel pufferelshez nincs elegend tr. Ha a calloc-tl tovbbi hely nyerhet jabb puffer ltrehozsra, akkor minden rendben van. Ha nem, akkor a -fillbuf puffereletlenbe- s kivitelt vgez egyetlen karakter hasznlatval, amelyet az egyik sajt tmbjben trol.

173

169

#include < stdio.h> fillbuf(fp) /*Bemeneti register FILE *fp; puffer lefoglalsa s feltltse*/

static char smallbuf [ NFILE]; _*Puffereletlen I/O-ra*/ ehar * calloc( ) ; if((fp-> --flag& READ)== 0 II (fp-7 flag& (-EOF II -ERR)) != 0) return(EOF); while (fp-> base == NULL) / *Pufferterletet keres* ; if (fp-_ flag& UNBUF) /*Puffereletlen*/ fp> -base = & smallbuf[fp-_ _fd]; elseif ((fp-_ base = calloc(-BUFSIZE, 1))==_LLLl fp-_ flag I = UNBUF; _ *Nem kap nagy puffert* _ else fp--_ -flag I = BIGBUF; ! * Nagy puffert kapott* ! fp-_ -ptr = fp- > base; fp -> cnt = read(fp-_ fd, fp--_ ptr, fp -> flag& UNBUF ? 1: -BUFSIZEl; if (--fp -> cnt C 0) { if (fp-> cnt =- --1 ) fp--] flag I = EOF.; else fp-> flag I = ERR; fp-_ cnt = 0; return(EOF); return(*fp-> ptr++ & 0377); _*.A karaktert pozitvv teszi*; A getc valamely llomnyra vonatkoz els hvsakor a darabszm 0, ami elidzi a fillbuf meghvst. Ha a _fillbuf gy tallja, hogy az llomny nincs olvassra megnyitva, azonnal az EOF rtkkel tr vissza. Egybknt megksrli a nagy puffer lefoglalst, s ha ez nem sikerl, az egykarakteres puffert utalja ki a -flag-beli pufferelsi informci rtelemszer belltsval. Ha egyszer a puffer ltrejtt, a fillbuf annak feltltsre egyszeren meghvja a read rutint, belltja a darabszmot s a mutatkat, majd a puffer kezdetn tallhat karakterrel tr vissza. A _fillbuf tovbbi hvsaikor a puf-fer mr rendelkezsre ll. Az egyetlen dolog, amit mg nem tisztztunk, hogy minden hogyan indul. Az stdin, stdout s stderr szmra definilni s inicializlni kell az iob tmbt:

174

170

FILE iob [ NFILEl ={ {NULL,_, NULL, READ, 0}, _* stdin */ {NULL, 0, NULL, WRITE, 1 }, / * stdout */ {NULL, 0, _ULL, WRITE _ UNBUF, 2 } / * stderr * / }; A struktra flag rsznek inicializlsa mutatja, hogy stdin-t ovasni, stdout-ot rni kell, stderr-re pedig pufferels nlkl runk. 8.3. Gyakorlat. Irjuk t fopen-t s fillbuf-ot gy, hogy explicit bitm-veletek helyett mezket hasznlunk! D 8.4. Gyakorlat. Tervezzk s rjuk meg a _flushbuf s fclose ruti-nokat! 0 8.5. Gyakorlat. A szabvnyos knyvtrban rendelkezsnkre ll az fseek(fp, offset, origin) fggvny, amely azonos az lseek fggvnnyel attl eltekintve, hogy fp llo-mnymutat s nem llomnyler. Irjuk meg fseek-et! Gondoskodjunk arrl, hogy az ltalunk rt fseek helyesen mkdjn egytt a knyvtr tbbi fggvnyei szmra vgzett pufferkezelssel! 0 8.6. Plda; katalgusok kilistzsa

Idnknt az eddigiektl eltr jelleg prbeszdet kell folytatnunk az llo-mnyrendszerrel: mag_ra az llomnyra vonatkoz informcira van szks-gnk, nem pedig arra, hogy mit tartalmaz az llomny. Plda erre az ls (list directory) nev UNIX parancs, amely kinyomtatja az adott katalgusban tallhat llomnyok nevt, s kvnsg szerint egyb informcit is kzl, mint pl. a mreteket, az engedlyeket stb. Mivel legalbbis a UNIX esetben a katalgus maga is egy llomny, semmi klns nincs az olyan parancsokban, mint az ls: beolvas egy llomnyt, s kiemeli belle a szmra fontos informcit. Ennek az informcinak a formtumt ugyanakkor maga a rendszer hatrozza meg, nem pedig a felhasznli program, gy az ls-nek ismernie kell az opercis rendszer brzolsmdjt. E megjegyzsek kzl nhnyat az fsize program megrsval fogunk szemlltetni. Az fsize az ls olyan specilis formja,amely az argumentumlistjban megnevezett sszes llomny mrett kinyomtatja. Ha az llomnyok valamelyike katalgus, az fsize erre rekurzvan alkalmazza nmagt. Ha egyltaln nem adtunk meg argumentumot, az aktulis katalgust dolgozza fel. Indulsknt rviden tismteljk az llomnykezelssel kapcsolatos

171

tudnivalkat. A katalgus (directory) olyan llomny, amely llomnynevek listjt tartalmazza, s utal arra, hogy a megfelel llomnyok hol tallhatk. 175

172

Az llomnyok cime valjban egy msik tblzatba, az inode tblzatba mutat index. Az llomny inodeja az a hely, ahol a nevet kivve az llo-mnyra vonatkoz sszes informci troldik. A katalgus bejegyzs csupn kt ttelt tartalmaz: az inode-szmot s az llomny nevt. A pontos specifikci a sys/dir.h llomny beiktatsval jn ltre, amelynek tartalma: #define DIRSIZ 14/*Az llomnynv max. hossza* / struct direct _ * A katalgusbejegyzs struktrja* _ ino_t d ino; _ * Inode-szm* / char d name[DIRSIZ); / *Allomnynv*/ Az ino_t tpus olyan typedef, amely az inode-tblzatba mutat indexet r le. A PDP =11 UNIX esetben ez unsigned, de ilyenfajta informcit nem szoks a programba gyazni: ms rendszerben ez eltr lehet. Innen a typedef. A rendszertpusok teljes kszlete a sys/types.h-ban tallhat. A stat fggvny veszi az llomny nevt, s az annak inode jban tall-hat sszes informcit (vagy hiba esetn -1-et) adja vissza. Eszerint : struct stat stbuf; char * name; stat(name, &stbuf); az llomnynvre vonatkoz inode informcival tlti fel az stbuf struktrt. A stat ltal visszaadott rtket ler struktra a sys/stat.h-ban tallhat, for-mja a kvetkez: struct stat _ *A stat ltal visszaadott struktra*_ dev t st-dev; /*Az inode perifrija* ino t st_ino; / * Inode-szm* / short st-mode; /* Md bitek* / short st nlink; /*Az llomnyra mutat linkek szma* I short st uid; /*A tulajdonds felhasznl azonostja*/ short st gid; /*A tulajdonos csoportjnak azonostja*/ dev_t st rdev; / * Specilis llomnyokra*/ off_t st size; [*Allomnymret karakterekben* / time- t st atime; /*Az utols hozzfrs idpontja* / time t st_mtime; /*Az utols mdosts idpontja* / time t st-ctime; / *Az eredeti itrehozs idpontja* / i; Ezek legtbbjt a megjegyzsek megmagyarzzk. Az st_mode bejegyzs az llomnyt ler jelzket tartalmaz; a knyelem kedvrt a jelzdefincik ugyancsak rszei a sys/stat.h-nak. 176

173

#define S_IFMT #define S IFDIR #define S_IFCHR #define S_IFBLK #defineS IFREG #define S ISUID vgre#define S #define S #define S #define S #define S ISGID ISVTX

0160000/* Az llomny tpusa* I 0040000 /*Katalgus* / 0020000/*Specilis karakter* / 0060000/* Specilis blokk* / 0100000/*Szablyos*/ 04000 / * Felhasznl azonost belltsa 02000 01000 haj tsra* _ _* Csonortazonost belltsa vgrehajtsra* / /*Az tvitt szveget hasznlat utn menti* / I *,Olvassi engedly* / /*Irsi engedly* / /,* Vgrehajtsi engedly* _

IREAD 0400 IWRITE 0200 IEXEC 0100

Most mr meg tudjuk rni az fsize programot. Ha a stat-tl kapott md azt jelzi, hogy az llomny nem katalgus, akkor a rendelkezsre ll mret kz-vetlenl kinyomtathat. Ellenkez esetben a katalgust llomnyonknt fel kell dolgoznunk: ez maga is tartalmazhat alkatalgusokat, gy a folyamat rekurzv. A frutin szoks szerint elssorban a parancssor-argumentumokkal foglalkozik: egy nagy pufferben ad t minden egyes arugmentumot az fsize fggvnynek. #include < stdio.h> #indlude < sys/types.h> / *typedef-ek* / #include < sys/dir.h> / * Katalgusbejegyzs struktra* / #include < sys/stat.h> /*A stat ltal visszaadott struktra*_ #define BUFSIZE 256 main(argc, argv) /* fsize: llomnymretek kinyomtatsa*/ char * argv[ ] ; char buf [BUFSIZE]; if (argc == 1 ) { /*Alaprtelmezs: az aktulis katalgus* / strcpy(buf, .); fsize(buf); } else while ( argc_0) { strcpy(buf, * ++argv); fsize(buf);

Az fsize fggvny az llomny mrett nyomtatja ki. Azonban ha az llo-mny katalgus, akkor elszr az sszes benne lev llomny kezelse rde-kben meghvja a directory fggvnyt. Figyeljk meg a stat.h-ban az S IFMT s S IFAIR jelznevek hasznlatt:

174

fsize(name) char *name;

/* Kinyomtatja a megadott nev llomny mrett* /

struct stat stbuf; if (stat(name, &stbuf) =- -1 ) { fprintf(stderr, fsize: %s nem tallhat\n, name); return; if ((stbuf.st mode& S IFMT) == S IFDIR) directory(name); printf(%old %s\n, stbuf.st size, name); A directory fggvny a legbonyolultabb.A legnagyobb rsze azonban a szban forg llomny teljes elrsi nevnek (pathname) ellltsval foglal-kozik. directory(name) _ * fsize a name-ben lev sszes llomnyra* ; char *name; struct direct dirbuf; char *nbp,*nep; int i,fd; nbp = name + strlen(name); *nbp++ = /; /* / hozzadsa a katalgus nevhez* _ if (nbp+DIRSIZ+2_ = name+BUFSIZE) / *A nv tl hossz* ! return; if ((fd = open(name, 0)) = -1 ) return; while (read(fd, (char *) & dirbuf, sizeof(dirbuf)) _ 0( if (dirbuf.d ino == 0) /*A rovat nincs hasznlatban* i continue; if (strcmp(dirbuf.d name, .) == 0 II strcmp(dirbuf.d name, ..) == 0) continue; szlt tugorja* ! for (i=0, nep=nbp; i C DIRSIZ; i++) *nep++ = dirbuf.d name[i]; *nep++ = \0; fsize(name); close(fd); * - nbp = \0; /*Nv helyrelltsa* _ /*nmagt s a

175

Ha a katalgus adott rovata. ppen nincs hasznlatban (mivel az llomnyt tneveztk), a md bejegyzs nulla, s ezt a pozeit tugorjuk. Minden kata-lgus tartalmazza bejegyzsknt nmagt a .nv alatt, valamint a szljt a .. nv alatt. Ezeket nyilvn t kell ugrani, klnben a program j ideig futni fog. Br az fsize program meglehetsen specilis, szmos fontos gondolatot mutat be. Elszr is, sok program nem rendszerprogram, csupn olyan infor-mcit hasznl, amelynek formjt vagy tartalmt az opercis rendszer kezeli. Msodszor, ilyen prograrxiok esetben lnyeges, hogy az informci brzolsa csak olyan szabvnyos, n. fej (header) llomnyokban jelenjen meg, mint stat.h s dir.h, tovbb, hogy a programok a konkrt deklarcik alkalmazsa helyett ezeket az llomnyokat iktassk be. 8.7. Plda; trterlet lefoglalsa

Az 5. fejezetben az alloc egyszerstett vltozatt mutattuk,be. A most meg-rand vltozat mr nem tartalmaz korltozsokat abban az rtelemben, hogy most az alloc s a free hvsai tetszleges sonendben kvethetik egy-mst, szksg esetn az alloc az opercis rendszertl ignyel tovbbi trter-letet. Ezek a rutinok nmagukban is hasznosak, emellett rvilgtanak arra, hogyan lehet gpfgg programokat viszonylag gpfggetlen mdon megrni, s a struktrk, az unionok, ill. a typedef vals letbl vett alkalmazsait is bemutatj k. Az alloc a helyfoglalst nem a program rszt kpez, rgztett mret tmbbl vgzi, hanem szksg szerint az opercis rendszeltl ignyel jabb trterletet. Mivel a porgramban foly egyb tevkenysgek aszinkron mdon ugyancsak ignyelhetnek helyet, elfordulhat, hogy az alloc ltal kezelt terlet nem lesz folytonos. gy a szabad terlet szabad blokkokbl ll lncot alkot. A blokkok a tulajdonkppeni szabad hely mellett egy mretet s egy, a kvetkez blokkot megcmz mutatt tartalmaznak. Nvekv trcm szerint kvetik egymst, s az utols (legmagasabb cm) blokk a legelsre mutat. Ily mdon a lnc valjban gyrt kpez. Trkrs esetn a program tvizsglja a szabad blokkok listjt, hogy tartalmaz-e elegenden nagy szabad blokkot. Ha a tallt blokk mrete ponto-san megegyezik a krt mrettel, akkor lekapcsolja a listrl s tadja a fel-hasznlnak. Ha a- blokk tlsgosan nagy, akkor a program kettvgja, s a felhasznlnak csak a megfelel mret terletet utalja ki, a maradkot pedig visszahelyezi a szabad listba. Vgl, ha nem tallt elegenden nagy blokkot, akkor jabb blokkot kr az opercis rendszertl, rkapcsolja a sza-bad listra, majd jra kezeli a keresst. A blokkfelszabadts szintn a szabad lista vizsglatval indul, a prog-ramnak ugyanis keresnie kell a listban egy olyan helyet, ahov a felszabad-tani kvnt blokkot beillesztheti. Ha a felszabadtott blokk brmelyik oidaln szomszdos egy listabeli blokkal, akkor a kett egyetlen, nagyobb blokk egyesl, gy a tr nem tredezik fel tlsgosan. A szomszdossg tnyt knnyen megllapthatjuk, hiszen a szabad listban a blokkokat cmnvekv sorrendben tartjuk nyilvn.

176

Az egyik problma, amit az 5. fejezetben rintettnk annak biztostsa volt, hogy az alloc ltal visszaadott terlet helyesen illeszkedjen azokhoz az objektumokhoz, amelyeket ott trolni kvnunk. Br a gpek klnbzek, minden gpen ltezik egy olyan tpus, amely, ha egy adott cmen trolhat, akkor ott az sszes tbbi tpus is biztosan trolhat. P1. az IBM 360/370, a Iloneywell 6000 s sok ms gp esetben brmilyen objektum trolhat olyan hatron, amely a double szmra, a PDP-11 esetben pedig az int szmra megfelel. A szabad blokkban a tulajdonkppeni szabad terletet megelz vezr-lsi informcit (a lncban kvetkez blokkot megcmz mutatt s a blokk mrett) fejnek nevezzk. Az illeszts egyszerstse rdekben minden blokk a fejmret tbbszrse, maga a fej pedig megfelelen illeszkedik. Ezt az albbi unionnal rhetjk el, amely tartalmazza a kvnt fejstruktrt, valamint a legnehezebben illeszthet tpusra vonatkoz kittelt: typedefint ALIGN; /*Illeszkedst biztost a PDP-11-en*_ union header{ / * Szabad blokk fej*_ struct{ union header * ptr; / * Kv. szabad blokk* / unsigned size; / * Ennek a szabad blokknak a mrete* _ }s; ALIGN x; / *A blokkok illesztse* / _; typedef union header HEADER; Az alloc rutinban a karakterekben elrt mretet felkerektjk a megfelel szm fejmret egysgg. A tnylegesen kiutalt blokk eggyel tbb ilyen egy-sget tartalmaz, t.i. egy egysgre magnak a fejnek is szksge van, s ez a da-rabszm kerl a fej size mezjbe. Az alloc ltal visszaadott mutat a szabad terletre mutat, nem pedig magra a fejre.

180

177

static HEADER base; / * res lista az indulshoz* / static HEADER *allocp = NULL; /*Az utols lefoglalt blokk*/ char * alloc(nbytes) / * Altalnos cl trfoglal* / unsigned nbytes; HEADER *morecore(); register HEADER *p, *q; register int nunits; nunits =1 + (nbytes+sizeof(HEADER)-1 )/sizeof(HEADER); if ((q = allocp) == NULL) {_ /*Mg nincs szabad lista*I base.s.ptr = allocp = q = &base; base.s.size = 0; for (p=q- _ s.ptr; ; q=p, p=p- _ s.ptr) if (p- _ s.size _= nunits){ if (p- _ s.size == nunits) q- _ s.ptr = p- 7 s.ptr; else { / * A hts felt foglalj a le* / /*Elg nagy* I /*Pontosan akkora* I

p- > s.size -= nunits; p += p- _ s.size; p- > s.size = nunits; allocp = Q; return((char * ) (p+ 1 )); if (p == allocp) I * Krljrtaa szabad listt* / if ((p = morecore(nunits)) == NULL) return(NULL); /* Nincs tbb* /

A base nev vltozt hasznljuk indulskor. Ha, mint alloc els hvsakor, az allocp rtke NULL, egy elfajult szabad lista jn ltre: egyetlen, nulla mret blokkot tartalmaz s sajt magra mutat. Ezutn a program minden esetben vgigkeresi a szabad listt. A megfelel mret szabad blokkot azon az (allocp) ponton kezdi keresni, ahol legutoljra tallt szabad blokkot; ez a stratgia elsegti, hogy a lista homogn maradjon. Ha a program tl nagy blokkot tall, akkor a felhasznl a blokk msodik felt kapja meg, ily mdon az eredeti fejben csak a mretet kell helyesbteni. A felhasznl-nak tadott mutat mindig a tnyleges szabad terletre mutat, amely egy egysggel a fej mgtt helyezkedik el. Figyeljk meg, hogy p karakterr alakul t, mieltt az alloc visszaadn. A morecore fggvny az opercis rendszertl kr trterletet. Ennek megoldsi mdja termszetesen opercis rendszertl fggen vltozik. A UNIX-ban az sbrk(n) rutin olyan mutatt ad vissza, amely n byte-nyi tr181

178

terletre mutat. (A mutat minden illeszkedsi megktsnek eleget tesz.) Mivel tr krse a rendszertl viszonylag kltsges mvelet, ezt nem akarjuk az alloc minden hvsakor megtenni, ezrt a morecore a krt egysgek szmt nagyobb rtkre kerekti fel; ezt a nagyobb blokkot aztn szksg szerint darabolhatjuk fel. A megnvels rtke olyan paramter, amely az ignyek-nek megfelelen vltoztathat. #define NALLOC 128/* Az egyszerre lefoglaland egysgek szma* / static HEADER *morecore(nu) /*Tr krse a rendszertl*/ unsigned nu; char *sbrk(); register char *cp; register HEADER *up; register int rnu; rnu = NALLOC * ((nu+NALLOC 1 ) / NALLOC); cp = sbrk(rnu * sizeof(HEADER)); if ((int) cp = -1 ) _ * Egyltaln nincs hely* / return(NULL); up = (HEADER *) cp; up- _ s.size = rnu; free((char * ) (up+ 1 )); return(allocp); Amennyiben nem volt hely, az sbrk -1-et ad vissza, br a NULL clszerbb vlaszts lett volna. A biztonsgos sszehasonlthatsg rdekben a = I -et int-t kell alaktani. Ismt srn hasznltuk a tpusmdostst, gy a fggvny viszonylag rzketlen az egyes gpek mutatbrzolsnak klnbzsgre. Maga a free utolsnak maradt. Egyszeren tvizsglja a szabad listt az allocp-tl kezdve, mikzben keresi a szabad blokk beillesztsre alkalmas helyet. Ez vagy kt, mr ltez blokk kz esik, vagy a lista vgn van. Ha a felszabadtand blokk brmelyik esetben szomszdos valamely msik szabad blokkal, akkor a program a kettt egyesti. Csupn arra kell gyelni, hogy a mutatk mindig a megfelel helyre mutassanak s a mretek helyesek legyenek!

182

179

free(ap)

/ *Az ap blokkot a szabad listba teszi* / char * ap ;

register HEADER *p, *q; p = (HEADER *) ap -1; _*A fejre mutat* / for (q=allocp; !(p _ q && p C q-_ s.ptr); q=q-7 s.ptr) if (q _= q_ s.ptr && (p > q I_ p C q-> s.ptr)) break; szddal* >s.ptr; /*Egyik vagy msik vgn*/ /*Egyesl a fels szomp-7s.ptr = q-_s.ptr/ p-7s.size += q-7s.ptr-_s.size; if (p+p-_s.size == q->s.ptr){

} else p-7s.ptr = q-_s.ptr; if (q+q- _s.size == p) /* Egyesl az als szomszddal* / q-_s.size += p ->s.size; q-7s.ptr = p-_s.ptr; } else q-_s.ptr = p; allocp = q; Br a trterletfoglals lnyegnl fogva gpfgg, a bemutatott program szemllteti, hogyan tarthatjuk kzben s korltozhatjuk a program egszen kis rszre a gpfgg vonatkozsokat. A typedef s az union segtsgvel gondoskodhatunk az sszeillesztsrl (feltve, hogy az sbrk a megfelel mutatt szolgltatja). A tpusmdost szerkezetek hasznlata explicitt teszi a mutatkonverzikat, s mg rosszul tervezett rendszercsatlakozssal is meg-brkzik. Noha az itt kzlt rszletek a trterletfoglalsra vonatkoznak, az elv, a megkzelts ms esetekben is alkalmazhat. 8. 6. Gyakorlat. A calloc(n, size) szabvnyos knyvtri fggvny n darab size nagysg objektumot megcmz mutatt ad vissza, a trterlet kezdeti nagysga nulla. Irjuk meg a calloc fggvnyt gy, hogy az alloc-ot mintaknt vagy hvott fggvnyknt hasznljuk! 8. 7. Cyakorlat. Az alloc a mretre vonatkoz krst anlkl fogadja el, hogy annak jogossgt ellenrizn. A free azt hiszi, hogy az a blokk, amely-nek felaszabadtst tle krik, rvnyes mret mezt tartalmaz. Javtsuk e programok minsgt azzal, hogy nagyobb gondot fordtunk a hibaellenr-zsre! 8.8. Gyakorlat. rjuk meg a bfree(p, n) rutint, amely az alloc s a free ltal kezelt szabad lista szmra felszabadtja az n karakterbl ll tetszleges p blokkot! bfree hasznlatval a felhasznl brmikor beiktathat a szabad lis-tba egy statikus vagy kls tmbt.

180

_ SZMALK Oktat- s Konzultcis Kzpont C programozsi nyelv II. TANANYAG Ksztette: Dombai Norbert Gpre vitte: Bognr Zoltn C programozsi nyelv II.

Direkt konzol I/O ----------------Alapja: conio.h header file

Lnyege, hogy minden billenty, illetve kperny mvelet pufferetlen, s tirnythatatlan. Ezrt sokkal gyorsabb, mint a standard file-ok kezelse. Lssuk a fggvnyeket!

1.

int getch()

Bekr egy karaktert a billentyzetrl. Echo nlkl, puffereletlen beolvass. Vissza: a beolvasott karakter kdja. /*Hibt nem ad vissza*/

2.

int getche()

u. a. mint a getch() csak echoval.

3. int c;

int ungetch /* karakterkd */

Visszateszi c karaktert a billentyzetpufferbe. Ksbb ezt ki lehet olvasni. Akr halkan is. /* pl. ch=getch() */ Vissza: a karakter kdja HIBA esetn :EOF.

4. int ungetch(c,fptr) int c; /* karakter */ FILE *fptr; /* file-pointer */

181

Visszateszi c karaktert az fptr file-ba. Pufferelt tvitelt kell ekkor megvalstani, s a file-nak olvassra kell megnyitva lennie! Vissza: a karakter kdja HIBA: EOF Figyelem! Semmi kze az ungetch()-hoz.

5.

int kbhit()

Ellenrzi a billentyzet-puffert, hogy van-e ott lettt karakter. A puffert nem trli. / Ksbb getch()-val vrakozs nlkl lehet felolvasni. / Vissza: 0, ha nincs semmi a pufferben nem 0, ha van valami.

6. char *cgets(s) char *s; /* trcm az input adatok szmra */ Kzvetlenl a konzolrl olvas be egy stringet, s elhelyezi azt s+2 cmtl kezdve. Hvsa eltt s[0] belltand; ide le kell tennnk a string maximlis hosszt. Olvass CR-LF/Enter-ig vagy(!) az adott hosszig / s[0] /; azontl spol. Enter-t /CR-LF/ \0-ra cserli. s[1]-be automatikusan elhelyezi a tnyleges hosszt. Vissza: pointer s+2cimre! HIBA esetn: NULL pointer Figyelem! Biztostand hossz: s[0]+3 minimun! 1. plda /C2P1.1 - String beolvassa kzvetlen konzolrl /

7.

void cputs(s) char *s; /* a string kezdcme */

Kir egy stringet /\0-al lezrva! / kzvetlen konzolra. Nem kezeli a CR-LF kombincit! Ez azt jelenti, hogy a \n csak j sorba lp, mg a sor elejre a \r-rel juthatunk. Vissza: semmi.

8.

int cprintf(...u.a....) ...u.a.... mint printf()-nl mr lttuk.

A printf megfelelje, de direkt konzol I/O-val mkdik. Hasznlja a putch() fggvnyt. Figyeljnk itt is \n s a \r kztti klmbsgre! Vissza: sikeres konverzik szma.

182

9 int cscanf(...u.a....) ...u.a.... mint a scanf()-nl mr lttuk. Kzvetlen a konzolrl valst meg formzott inputot. Httrben a getche() fggvnyt hasznlja. Vissza: sikeres konverzik szma EOF=EOF esetn; 0 ha hiba volt.

10. void putch int c; /* kirand karakter kdja */ Kir egy karaktert /c/ kzvetlen konzolra. Vissza: semmi. A file-kezels alapjai: ---------------------- standard file-ok / stdin, stdout, stdprn, stdaux, stderr / lemezes llomnyok mint egy byte-halmaz jelenik meg nincs file-tpus, mindg az adott alkalmazs dnti el, hogy milyen tpusnak veszi a file-t. Az stdio.h headerfile fontosabb rszei: ... /* (ANSI / System V) # UNIX alatti fejlesztshez! ... ... */ #define #define #define #define #define #define #define #define BUFSIZ _NFILE EOF FILE P_tmpdir SEEK_CUR SEEK_END SEEK_SET 512 20 (-1) /* mirt kell zrjel? */ struct_iobuf // 1 2 0 20

#define SYS_OPEN

# if (defined(M_I86SM) || define(M_I86MM)) #define NULL defined(M_I86HM)) #define NULL #endif Megjegyzs: V6.00 = ((void*)0) /* file kontroll block */ 0L 0 # elif (defined(M_I86CM) || define(M_I86LM) ||

183

# ifndef _IOB_DEFINED extern FILE { char *_prt; /* rsi/olvassi pointer */ int _cnt; /* karakterek / helye a pufferbe / */ char *_base; /* a puffer kezdcme */ char _flag; /* a file llapota (hiba: EOF) */ char _file; /* file handle */ } _NEAR _CDECL _iob[]; #define_OIB_DEFINED #endif #define #define #define #define #define #define fpos_t stdin stdout stderr stdaux stdprn long /* file pozci */ (&_iob[0]) (&_iob[1]) (&_iob[2]) (&_iob[3]) (&_iob[4])

/* Fggvny ill. makrdefincik. */ #define getc(f) (--(f)->_cnt>=0 ? 0xff & *(f)->_ptr++ :_filbuf(f)) /* Beolvas struktra; egy karaktert f-el azonostott file-bl. f file

lsd fentebb! */ #define putch(c,f) (--(f)->_cnt >=0 ? 0xff & *(f)->_ptr++=\ (char)) :_flsbuf(,(f)) /* c vltozbl azonostott egy karaktert kir a f (FILE*) vltozval

file-ba. _filbuf(),_flsbuf() bels file-kezel fggvnyek. */ #define #define #define #define #define getchar() putchar() feof(f) ferror(f) fileno(f) getc(stdin) putc(,stdout) ((f)->_flag &_IOEOF) ((f)->_flag &_IOERR) ((f)->_file)

/* elzekben */ #define #define #define #define _IOEOF _IOERR _IOREAD _IOWRT 0x10 0x20 0x01 0x02 /* /* /* /* elrtk EOF-et */ hiba van */ olvassra nyit */ rsra nyitva */

File-kezels II. szint: ----------------------Karakteres/szveges md: -----------------------/ alapja stdio.h headerfile /

184

11.

FILE *fopen(fazon,md) char *fazon; /* file azonostja */ char *md; /* hasznlati md */

Megnyitja fazon-nal azonostott nev file-t. Vissza: file-pointer sikeressg esetn NULL pointer hiba esetn. Md: Ltez file megnyitsa olvassra. /Nincs akkor hiba./ w: res file nyitsa rsra. /Ha ltezett, akkor trli az eredeti tartalmt./ a: Ltez file megnyitsa hozzfzsre. /Nem ltez file esetn ltrehozza./ r+: Ltez file-t megnyit rsra olvassra. w+: res file-t megnyit rsra olvassra. a+: Ltez file-t nyit olvassra s hozzrsra. /Ltez adatoto nem rhat fell./ /* MSC-ben: */ t: Text md. b: Binris md. Text: input: CR+LF -> LF (\n) output: LF -> CR+LF r:

12.

FILE *freopen(fazon,md,fptr) char *fazon; /* j file azonostja */ char *md; /* hasznlati md */ FILE *fptr; /* rgi file pointer-re */ md

Az fprt-rel azonostott file-t lezrja, s fazont megnyitja hasznlati mddal. Az jonnan nyitott file-t is fptr azonostja. Vissza: lsd elbb.

13. int fclose(f) FILE *f; Lezrja f-fel azonostott file-t. Minden puffert rt s trl. Vissza: 0 - sikeres zrs EOF - hiba esetn.

14.

int fcloseall()

Minden nyitott file-t lezr puffer rtssel. Kivve a standard fileok. Vissza: Lezrt file-ok szma EOF - Hiba esetn.

185

15. int ferror(f) FILE *f; Megvizsglja, hogy trtnt-e I/O hiba. Hiba esetn a hibajelz bekapcsolva marad, amg le nem zrjuk a file-t, vagy nem hvjuk a clearerr() fggvnyt. Vissza: 0 - nincs hiba nem 0 - hiba volt.

16. int fflush(f) FILE *f; Output: rti a puffereket melyek f-hez tartoznak. Input: Trli a puffert.

Vissza: 0 - sikeres EOF - hiba volt. Irs s olvass:

17. int getc(f) FILE *f; Az f-fel azonostott karaktert. file-bl beolvassa a soron kvetkez egy

Vissza: a beolvasott karakter kdja EOF - hiba, vagy file-vg esetn.

18.

int putc(c,f) char c; /* Kirand karakter kdja */ FILE *f; /* File-hoz tartoz pointer */

Az output file f aktulis pozcijba kirja c-t. Vissza: a kirt karakter kdja Hiba: EOF-et ad, hvjuk ferror()-t!

19. int fgetc(f) FILE *f; U.a. mint getc(), de megvalstsa fggvny s nem makr! [ Az fgetchar() - stdin-rl, szintn fggvny! ]

20. int fputc(c,f) char c;

186

FILE *f; U.a. mint a putc(), csak fggvny. [ Az fputchar() - stdout-ra, szintn fggvny! ]

21. int feof(f) FILE *f; Az f-fel azonostott file-ra file-vget vizsgl. Ha egyszer filevget szlel, akkor minden olvassnl EOF-et ad. Vissza: 0 - EOF mg nincs nem 0 - EOF van. 2. plda: /C2P2.C - C2P2A.C - C2P2B.C szvegfile-ok kezelse./ Figyelem! std.. file-okat nem kell nyitni s zrni. Formzott I/O:

22. int fprintf((f,...u.a....) FILE *f; ...u.a.... = mint a printf()-nl Formtumozott adattvitelt hajt vgre az f-el megadott file-ra. Vissza: az tvitt karakterek szma.

23. int fscanf(f,...u.a....) FILE *f; ...u.a.... = mint a scanf()-nl Formtumozott adattvitelt hajt vgre f file-bl. Beolvas. Vissza: sikeresen konvertlt s tvitt objektumok szma EOF hiba, vagy file-vg esetn. Tovbbi I/O lehetsgek:

24.

char *fgets(string,n,f) char *string; /* buffer az adatoknak */ int n; /* max. beolv. karakter */ FILE *f; /* file azonost */

Beolvas a file-bl / aktulis pozcitl / n -1 karaktert, ill. az els \n-ig! Attl fggen, hogy melyik teljesl elbb. Figyelem: a beolvasott\n-t is trolja. A vgre \0-t tesz! A rokon gets() NEM trolja \n-t!

187

Vissza: Pointer a stringre NULL: hiba vagy EOF esetn.

25.

int fputs(s,f) char *s; /* kirand string */ FILE *f; /* file-t azonost pointer */

Az output file f aktulis pozcijtol kezdi kirni a stringet s. A \0-t nem viszi t. Vissza: az utoljra tvitt karakter 0, ha a string res EOF hiba esetn. 3. plda / szvegfile olvassa C2P3.C, C2P3A.C, C2P3B.C / 4. plda / szvegfile kezelse C2P4.C / File kezels binris mdon: ---------------------------

26.

int fwrite(puffer,mret,db,f) unsigned char *puffer; /* honnan */ unsigned int meret; /* obj. mrete */ unsigned int db; /* obj. darabszma */ FILE *f; /* file azonostja */ pozcitl) db-nyi mret hosszsg

Kir a file-ba (aktulis objektumot a pufferbl.

Vissza: tvitt objektumok szma Hiba: kisebb mint db. Megjegyzs: text mdnl LF CR-LF. Tapasztalatok: text mdnl nmi bizonytalansg.

27.

unsigned int fread(puffer,mret,db,f) unsigned char *puffer; /* honnan */ unsigned int meret; /* obj. mrete */ unsigned int db; /* obj. darabszma */ FILE *f; /* file azonostja */

Beolvas aktulis pozcitl mret-hossz db-nyi adatot, s leteszi a puffer-be. Vissza: beolvasott objektumok szma HIBA esetn kevesebbet ad mint db. Megjegyzs: Text llomnynl CR-LF helyett LF-et tesz le, de nem kezeli a \0-t.

188

28. long ftell(f) FILE *f; Megadja az f file aktulis pozcijt a file elejhez kpest byteban. Vissza: HIBA esetn -1L hatrozatlan, ha nincs nyitott file-hoz rendelve f. / pl: stdprn /

29.

int fseek(f,eltols,honnan) File *f; /* file-pointer */ long eltols; /* byte-ban mennyit */ int honnan; /* pozci */

A file pointert az adott f file-ban a kvnt pozciba mozgatja. honnan: SEEK_SET - file eleje SEEK_CUR - aktulis pozci SEEK_END - file vge Vissza: 0 sikeres nem 0 - hiba hatrozatlan, ha a mvelet rtelmetlen. (pl.: stdprn) Figyelem: soremels (CR-LF) 5. plda / C2P5.C - megadott file utols 10 sornak listzsa / 6. plda / C2P6.C - nevek, s telefonok felrsa, visszaolvassa. /

30. void rewind(f) FILE *f; Ujranyitja az f file-t! A file elejre pozcionl, puffert rt, ha kell [ fseek(f,0l,SEEK_SET); ], de a rewind() trli az EOF-et s a hibajelzket is. Vissza: nincs. Segdfggvnyek: ----------------

31. int fileno(f) FILE *f; Visszaadja az f file file-handle szmt. Ha tbb is tartozik hozz, akkor az els open-nek megfelelt. Vissza: File-handle hatrozatlan, ha nem tartozik nyitott file-hoz.

189

32. long filelength(fh) int fh; /* file-handle szm */ Vissza: a file hossza byte-ban 1L rvnytelen fh esetn.

33. int flushall() Minden outputra nyitott file puffert lemezre rti; input file-nl pedig trli. Vissza: a file-ok szma amennyit rtett. 7. plda / C2P7.C - tmbk kirsa file-ba, filehossz kirsa / File-kezels I. /alacsony szint/: -------------------------------- file-handle szm file, mint byte tmb elssorban binris file-kezels (ekkor semmi konverzi nincs, ellenttben a szvegmddal!) Alapja: fcntl.h io.h types.h stat.h } } } } header file-ok

34.

int open(nv,oflag[,md]) char *nv; /* a file neve */ int oflag; /* opercis kd */ int md; /* hasznlati md */

Megnyitja a nv nev file-t oflag fggen. oflag lehetsges llapotai fcntl.h-ban: O_APPEND=hozzrs. File-pointert a file vgre teszi. O_CREAT =ltrehozza. Ha ltezett akkor fellrja. O_EXCL =Ha van nem rjuk fell. Rendszerint O_CREAT-tal egytt hasznljuk. O_RDONLY=csak olvassra nyitja a file-t. Nem hasznlhat az O_WRONLY-val egytt, s az O_ROWR-rel sem. O_ROWR =rs/olvassra nyitja a file-t. Nem hasznlhat az O_RDONLY-val, s az O_WRONLY-val egytt. O_WRONLY=csak rsra nyitja a file-t. Nem hasznlhat az elbbi kettvel egytt. O_TRUNC =nyitja, s lenullzza a file hosszt. Tartalom elvsz.

190

Ir mvelettel egytt szoks hasznlni. O_BINARY=binris pufferetlentvitelt valst meg. O_TEXT =szveges, pufferelt tvitelt valst meg.

Megjegyzsek: O_TEXT default a hasznlati mdot csak O_CREAT esetn kell megadni O_WRONLY MS-DOS rtelmetlen, de 3.0 vagy magasabb verzi esetn ha installljuk a SHARE kls parancsot, akkor O_CREAT-tel hasznlhat. Hasznlati md: --------------/ stat.h. header file-ban / S_IWRITE =rs engedlyezse S_IREAD =olvass engedlyezse S_IREAD|S_IWRITE=rs/olvass engedlyezse ( bitenknti vagy ) Vissza: file-handle szm 1: hiba esetn, a tnyleges hibakd az errno vltozban van. errno:extern int errno; extern int _doserrno; (stdlib.h vagy stddef.h-ban is van) /* ltalnos I/O hiba */ /* DOS funkcik */

35. int creat(nv,md) char *nv; /* a file neve */ int md; /* hasznlati md */ Nemltez file-t ltrehoz, vagy ltezt fellr. Md lsd open()-nl. Vissza: file-handle hiba: -1,kd az errno-ban. Figyelem: ha MS/PC-DOS krnyezetben S_IWRITE-tal hozunk ltre egy file-t, akkor mg rhatunk be, de lezrs utn mr csak olvashat lesz. De azt aztn meg lehet szntetni. Lehetsges hibk: ----------------/ alapja errno.h - mind #define.../ EACCESS = DOS 3.0 vagy magasabb verzik! Nem osztott file elrse; read-only-t rsra probltak nyitni. EEXIST EMFILE ENDENT = Ltezik a file. = Tl sok file van megnyitva. = File, vagy az tvonal nem tallhat.

36. int close(fh) int fh; /* file-handle szm */ Lezrja az fh-val azonostott file-t.

191

Vissza: 0 - Ok 1 - hiba errno=EBADF

37. int write(fh,puffer,count) int fh; /* file-handle szm */ char *puffer; /* adatok kezdcme */ unsigned int count; /* kirand byte-ok szma */ Kir puffer kezdcmtl count-nyi byte-ot az fh-val azonostott file aktulis pozcijtl kezdve. Vissza: Kirt byte-ok szma HIBA: -1 lsd errno-t EACCESS EBADF ENOSPC - nincs hely Figyelem: egyszerre maximum 65534 byte-ot lehet kirni!

38.

int read(fh,p,c) int fh; char *p; unsigned int c;

/* file-handle szm */ /* mutat a pufferre */ /* olvasand byte-ok szma */

Beolvas s elhelyezi p cmtl kezdve c-nyi byte-ot. Vissza: Beolvasott byte-ok szma HIBA: -1 lsd errno-t Figyelem: egyszerre maximum 65534 byte-ot lehet olvasni! write/read ha O_TEXT-ben megy CR-LF --> LF (read) LF --> CR-LF (write) MS-DOS alatt text md esetn file vge: ^Z, ekkor 0-t ad vissza.

39. long tell(fh) int fh; /* file-handle szm */ Visszaadja a file aktulis pozcijt byte-ban. HIBA: -1 lsd errno-t.

40. long lseek(fh,offs,origin) int fh; /* file-handle szm */ long offs; /* hov byte-ban */ int origin; /* honnan */ A fseek() alacsony szint megfelelje. Pozcionl a file-ba. SEEK_SET

192

SEEK_CUR SEEK_END hasznlhat. Vissza: File pozci HIBA: -1 lsd errno-t.

41. int eof(fh) int fh; /* file-handle szm */ A fggvny EOF-et vizsgl alacsony szinten! Vissza: 1 EOF esetn 0 ha nem EOF 1 hiba lsd errno-t.

42. int sopen(nv,oflag,shflag[,md]) char *nv; /* a file neve */ int oflag; /* opercis kd */ int shflag; /* osztott elrsi mdok */ int md; /* hasznlati md */ Minden megegyezik az open()-nal, de osztott krnyezetben! Fontos: share.h! shflag: ------SH_COMPAT SH_DENYRW SH_DENYWR SH_DENYRD SH_DENYNO = = = = = kompatibilis md lltsa rs s olvass tiltsa rs tiltsa olvass tiltsa rs/olvass is engedve

Fontos: csak MS-DOS 3.1 vagy magasabb verzikban. Eltte installlni kell a SHARE.EXE (vagy SHARE.COM) kls DOS parancsot! Kompatibilis md az op.rendszer verzik thidalsra. Vissza: file-handle szma HIBA -1 lsd errno: EEXIST: O_CREAT! O_EXCL-nl ltezik. Pufferelt file-oknl nem korrekt a megvalsts!!

43. int locking(fh,md,byte) int fh; /* file-handle szm */ int md; /* zrolsi md */

193

long byte;

/* lock-olt byte-ok szma */

Zrol, vagy felszabadt az fh file byte-nyi byte-jt. Byte szmlls az aktulis file-pointer pozcitl indul. Csak MS-DOS 3.0 vagy magasabb verziknl hasznlhat! Lock-olsi mdok: locking.h-ban ------------------------------LK_LOCK LK_RLCK LK_NBLCK LK_UNLCK = zrolja a byte-okat (tbbszr prblkozik...) 10 x 1. = u.a. mint az elbb. = zrolja a byte-okat egyszer prblkozik, majd hibt ad. = felszabadtja a lock-olt (!!) byte-okat.

LK_NBRLCK = u.a. mint az elbb. Figyelem: az sszes zrolst fel kell oldani mieltt lezrjuk a filet, vagy kilpnk a process-bl. Vissza: 0 = O.K. HIBA = -1 lsd errno: EDEADLOCK = LK_LOCK sikertelensge esetn EINVAL tartomnyt adtunk! 8. plda / C2P8.C - alacsony szint file-kezels / 9. plda / C2P9.C - locking plda / Egyb file-kezelst segt fggvnyek: 44. int chdir(path) char *path; / dekl.: direct.h / /* katalgus neve */ = rvnytelen

Knyvtrat vlt, mint az MS-DOS parancs. Vissza: 0 = O.K. HIBA = -1 - nem ltez path.

45. int rename(rgi,j) char *rgi; /* a file rgi neve */ char *uj; /* a file j neve */ File illetve katalgus tnevezse ( MS/PC-DOS szabvnyos nv ) Vissza: 0 = O.K. HIBA = -1 - lsd errno-t.

46. int unlink(file) char *file; /* a file neve */ A file nev file trlse. Vissza: 0 = O.K. HIBA = -1 - lsd errno-t. / lsd mg remove() /

194

47. int remove(file) char *file; /* a file neve */ Trli a megadott file nev file-t. Vissza: 0 = O.K. HIBA = -1 - lsd errno-t. 48. int mkdir(path) char *paht; / dekl.: direct.h / /* az j katalgus neve */

Ltrehoz egy j alknyvtrat. Vissza: 0 = O.K. HIBA = -1 - lsd errno-t. 49. int rmdir(path) char *paht; / dekl.: direct.h / /* a trlend katalgus neve */ melynek resnek kell lennie! Aktulis

Trli az alknyvtrat, katalgus sem lehet. Vissza: 0 = O.K.

HIBA = -1 - lsd errno-t. 50. int setmode(fh,md) int fh; int md; /* file-handle szm */ /* j tviteli md */

Belltja az fh-file tviteli mdjt: O_TEXT vagy O_BINARY. Vissza: elz md rtke - O.K. 1: HIBA, lsd errno-t. 50.a. char *getcwd(path,n) dekl.: direct.h char *path; /* puffer a katalgusnvnek */ int n; /* a path nv maximlis hossza */ Felemeli a teljes elrsi utat, ami pp az aktulis munkakatalgusunk. Az n = buffer hossz-1, most a \0-nak is kell hely! Ha path-nak NULL-t adok t, akkor automatikusan hvja malloc()ot s helyet foglal. Felszabadts free()-vel visszaadott ptr -rel! Vissza: mutat a puffer kezdcmre NULL : hiba, lsd errno-t ERANGE: tvonalnv hosszabb, mint n byte ENOMEN: nincs elg memria 13. plda / C2P13.C - knyvtrvlts + file trlse / Memriakezels: ---------------

195

memria modellek dinamikus trkezels Memria modellek: az Intel processzorok miatt 6 modell ltezik 8 illetve 16 bites pointerezs a.) small: max. 64 Kb kd max. 64 Kb adatszegmens max. 64 Kb adatszegmens 1 Mb adatszegmens 1 Mb adatszegmens

b.) medium: 1 Mb kd ( tbb kdszegmens ) c.) compact: d.) large: max. 64 Kb kd 1 Mb kd

(szegmensenknt egy adat mrete gy is max. 64 Kb)

e.) huge: 1 Mb kd 1 Mb adatszegmens (de! lehetsg van 64 Kb-nl nagyobb adatok kezelsre!) f.) tiny: kd+adat+stack <= 64 Kbyte (COM tpus) C-ben a stack szegmens mindg az adatszegmens rsze! Default rtke 2 Kbyte. lltsa linker opci: link... /ST:bytes Szegmensek: ----------Kd: _TEXT; tbb forrs modul esetn modulnv\_TEXT visszatrsi cmek, loklis (auto) el nem ksztett (inicializlatlan)

Stack: STACK: vltozk helye. Adat: _BSS:

statikus (static) vltozk. Fordt mindent binris nullval tlt fel. C_COMMON: vltozk. CONST: konstans adatok, melyek elfordulnak a programban, kivtel a szvegkonstansok. _DATA: inicializlt globlis (kls) s a NULL: az adatterlet fizikai szegmensnek els nhny byte-ja. Tartalmt a program kilpskor ellenrzi! inicializlatlan kls (globlis)

statikus (static) vltozk. Tovbb a szvegkonstansok.

Fontos: szegmensek szma: tbb forrs modul esetn tbb is lehet. Default: 128. lltsi lehetsg linker opci: link... /SEG: number. 51. char *malloc(m) unsigned int m; dekl.: malloc.h /* byte-ok szma */

196

Lefoglal m mret (byte) memriablokkot a near heap-ben. Eltr tpus esetn hasznljuk a cast opertort. Vissza: NULL: nincs elg memria vagy pointer az alloklt blokk elejre. 52. void free(p) char *p; Felszabadtja a korbban alloklt memrit. Fontos, hogy a p mutat a felszabadtand memria kezdetre mutasson! Hvhat: malloc(), calloc(), realloc() utn! Vissza: nincs. dekl.: malloc.h

53. char *calloc(n,mret) unsigned int n; /* elemek szma */ unsigned int mret; /* egy elem mrete byte-ban */ Lefoglal n darab mret nagysg memriablokkot. Tipikus alkalmazsa a dinamikus tmbk megvalstsa. Vissza: pointer a terlet kezdetre NULL: nincs elg hely a memriban. 54. char *realloc(p,mret) char *p; /* a lefoglalt terlet kezdcme */ unsigned int mret; /* j mret byte-ban */ A p-hez rendelt, mr elzleg lefoglalt memriablokk nagysgt mretnyire mdostja. Vissza: NULL: nincs elg memria, vagy mret=0, pointer a lefoglalt blokk elejre. 14. plda / C2P14.C visszaolvassa / lefoglal memrit, feltlti,

55. int memicmp(mit,mivel,hossz) char *mit,*mivel; unsigned int hossz; sszehasonltja mit-et mivel-lel hossz-nyi hosszan. Nincs klmbsg kis s nagybet kztt! Vissza: <0, ha mit < mivel 0, ha egyenl >0, ha mit > mivel

56. int memcmp(mit,mivel,hossz) char *mit,*mivel;

197

unsigned int hossz; sszehasonltja mit-et mivel-lel hossz-nyi hosszan.

57. char *memcpy(vev,ad,db) char *vev; /* output kezdcm */ char *ad; /* input kezdcm */ unsigned int db; /* mennyit */ tmsol ad-bl vev-be db-nyi byte-ot. Vissza: pointer vev-re.

58. char *memset(puffer,kar,db) char *puffer; /* kezdcm */ int kar; /* milyen karakterrel */ unsigned int db; /* mennyit */ Puffer els db-nyi byte-jt kar karakterrel tlti fel. Vissza: pointer puffer-re.

59. char *memccpy(vev,ad,c,db) char *vev,*ad; int c; unsigned int db; Byte-okat msol ad-bl vev-be c-vel bezrlag, vagy(!) db-nyit. Amelyik elbb teljesl. Vissza: pointer c-t kvet byte-ra NULL, ha nem msolta c karaktert.

60. char *memchr(puffer,c,db) char *puffer; /* honnan kezdden */ int c; /* mit */ unsigned int db; /* milyen hosszan */ Megkeresi puffer-ben az els db-nyi byte kztt a c karaktert (els elfordulst). Vissza: pointer a megtallt c-re NULL, ha nincs.

61. void unsigned int unsigned int unsigned int

movedata(si,oi,so,oo,db) si,oi; /* input segment+offset */ so,oo; /* output segment+offset */ db; /* mennyit */

198

Byte-ok msolsa / db-nyit /. Far tpus adatok msolsra small s medium modelleknl. Persze ms modellekben is hasznlhat. Figyelem: csak IBM PC-k esetben van implementlva! Vissza: semmi. unsigned int FP_OFF(adress) unsigned int FP_SEG(adress) char far *adress; dekl.: dos.h dekl.: dos.h

/* tvoli mutat a memriacmre */

Szegmens s offszet adat generlsa. Vissza: a cm segment illetve offset rsze. Segtsg: --------char ptr tvoli mutat far *ptr; = (char far *) 0xB800 0000; cast szegmens offszet rtkek

[Ez a szvegmd -sznes monitor ltal hasznlt memria kezdcme.] Orisi blokkok alloklsa: 62. char huge *halloc(n,meret) long n; unsigned int mret; Trterletet allokl n elem mret nagysg huge tpus tmbknek. A terlet 0-val lesz feltltve. Ha n*mret > 128K, akkor mret = 2k (k=pozitv egsz hatvnykitev!). A terlet a far heap-bl lesz levgva. Vissza: huge mutat a memriaterlet elejre NULL: nincs elg memria. 63. void hfree(p) char huge *p; Felszabadtja a halloc() ltal lefoglalt memriablokkot. Vissza: semmi. 64. char *_fmalloc(m) unsigned int m; dekl.: malloc.h /* mennyit */ dekl.: malloc.h dekl.: malloc.h

Allokl m-nyi byte-ot a standard adatszegmensen kvl, a far heap terleten. Vissza: pointer a terlet kezdetre NULL: nincs elg memria. 65. void _ffree(fp) char far *fp; dekl.: malloc.h /* mutat a memriablok kezdetre */

199

Felszabadtja az fp ltal megcmzett memriablokkot. Vissza: semmi.

15. plda /C2P15.C - kpments / 16. plda /C2P16.C - kpments - feltlts /

Process-kezels: ---------------Ebben a rszben azokkal a fggvnyekkel ismerkednk meg, amelyek lehetv teszik, hogy egy C programbl egy msik C programot, vagy akr brmilyen ms binris programot indtsunk. 66. int system(parancs) dekl.: process.h vagy stdlib.h char *parancs; /* az indtand program neve */ Vgrehajtja a parancs-ban megadott nev parancsot. PC/MS-DOS vagy aktulis op. rendszer-beli parancs. A COMMAND.COM-on keresztl dolgozik. (UNIX: subshell-t tlt!) Vissza: 0 - O.K. 1 - hiba, lsd errno-t. 67. void exit(stat) stdlib.h int stat; dekl.: process.h vagy /* visszatrsi kd */

Befejezi a program futtatst. Ha van hv program, akkor a stat als byte-jt (stat & 0xFF) megkapja. Futs eltt befejezi az onexit fggvnyeket, minden puffert rt, lezrja a file-okat. Figyelem: main() vgn automatikusan exit(0) hajtdik vgre! Vissza: semmi. 68. void _exit(stat) int stat; Azonnali lells fggvny. Vissza: semmi. 69. void abort() dekl.: stdlib.h Abnormal program /* visszatrsi kd */ trlsek, zrsok nlkl! Egybknt mint exit()

Azonnali lells minden zrs, rts nlkl! termination zenetet r ki. exit sttusz: -3. 70. onexit_t onexit(func) onexit_t func; /* kilp fggvny */

dekl.: stdlib.h

200

A fut program befejezsekor az elzleg belltott (onexit()-tel) fggvnyeket LIFO elv szerint vgrehajtja. Kivve abort() s _exit()! Vissza: pointer a fggvnyre! NULL, ha nincs tbb hely a fggvny szmra.

17. plda /C2P17.C - C2P17A.C - onexit / typedef int (_CDECL*CEDCL onexit_t)(); Process hv utastsok: / Alap : process.h / 71. int execl(name,arg0,arg1,...,argn,NULL) 72. int execle(name,arg0,arg1,...,argn,NULL,env) 73. int execlp(name,arg0,arg1,...,argn,NULL) 74. int execlpe(name,arg0,arg1,...,argn,NULL,env) 75. int execv(name,argv) 76. int execve(name,argv,env) 77. int execvp(name,argv) 72. int execvpe(name,argv,env) Deklarcik: -----------char *name; */ char *arg0,*arg1,... */ char *argv[]; */ char *env[]; tmbje */ /* /* /* pointerek paramterek a egy program neve

paramterekre pointertmbben

/* krnyezeti stringekre mutat pointerek hvsi Ha nem XXX. teljes vgig

Betltenek, s vgrehajtanak egy j gyerekfolyamatot. Ha a mechanizmus j, akkor a hv helyre tltdik az j process. adunk meg kiterjesztst, gy EXE-nek veszi. Ha viszont a nv teht ponttal r vget, akkor nem tallja meg. Nv lehet akr elrsi ttal is megadva. Az execlp(), execlpe(), execvp() esetben a DOS keresi a programot, ha az aktulisban nem tallta! Paramterek: arg0 = prog.nv (hvand) egyttes paramterhossz: 128 byte NULL, mint lezr ktelez. Vissza: siker esetn semmi HIBA: -1, lsd errno-1. HIBK: -----EZBIG EACCESS EMFILE ENOMEM = = = = 128-nl hosszabb paramterek osztott elrsi problma tl sok file van nyitva nincs elg memria PATH-ban

201

ENOEXEC = nem futtathat program ENOENT = file, vagy path nem tallhat Megjegyzs: krnyezeti rklds van, de a nyitott file-ok tviteli mdja nem rzdik meg. A setmode() fggvny hasznlhat ekkor. 79. 80. 81. 82. 83. 84. 85. int int int int int int int spawnl(md, spawnle(md, spawnlp(md, spawnv(md, spawnve(md, spawnvp(md, spawnvpe(md, ) ) ) ) ) ) )

} u.a. mint az exec...- nl

Minden u.a. mint az exec...-nl, de a md paramter befolysolja a mkdst. Md: szl-gyerek process kapcsolat ---P_WAIT P_NOWAIT P_OVERLAY = szl vr, mg a gyerek fut! = konkurrens vgrehajts. = ngyilkos szl esete; megegyezik exec...-el!

Vissza: exit sttusz rtke: exit(), _exit()-el adhat vissza 1 HIBA, lsd errno-t

18. plda / kapcsolat /

C2P18.C

C2P18A.C

szl

gyerek

process

Rendszerid kezelse: --------------------86. char *ctime(tsec) const time_t tsec; dekl.: time.h /* numerikus idrtk */

( time.h-ban: typedef long time_t, / const = megvltoztathatatlan rtk /) A long tsec rtkknt trolt idt karakterlncc konvertlja. Az aktulis tsec-et a time(time_t) dtum-fggvny segtsgvel kapjuk. Vissza: 1970. jan. 1. 0 ra 0 perc 0 mp (Greenwitch) ta eltelt idt adja sec-ben a time() fggvny. Ebbl csinl a ctime() 26 karakteres lncot: Sun Dec 21 12:43:00 1988\n\0. Erre mutat! 87. time_t time(timeptr) time_t * timeptr; /* ide trolja az idt numerikusan */

202

Vissza: NULL, ha nem sikerlt az rtket kpeznie!

19. plda / C2P19.C segtsgvel /

dtum

kiratsa

time()

ctime()

88. char *asctime(time) struct tm{ int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mod; int tm_year; int tm_wday; int tm_yday; int tm_isdst; } *time; /* /* /* /* /* /* /* /* /*

dekl.: time.h msodpercek (0 - 59) percek (0 - 59) rk (0 - 23) hnap napja (0 - 31) hnap (0 - 11) v 1900 - tl nap szma (0 - 6) 0=vas. nap szma Jan 1-tl flag */ */ */ */ */ */ */ */ */

A 26 karakteres stringg konvertlja a struktrban megadott idt! Vissza: mutat a stringre. 89. char *_strdate(date) char *date; /* memria a dtumnak Leteszi date-be a gp aktulis dtumt: Vissza: mutat a memriaterlet kezdetre. 90. char *_strtime(time) char *time; /* memria az idnek */ /pp/mm formban. dekl.: time.h */ HH/NN/ formban.

Leteszi time-ba a gp aktulis idejt: Vissza: mutat a memriaterlet kezdetre.

20. plda / C2P20.C - alkalmazzuk _strtime() fggvnyeket /

az

_strdate()

az

Nhny rdekes beptett fggvny: ---------------------------------91. void qsort(base,num,width,comp) dekl.: search.h char *base; /* rendezend adatok kezdcme */ unsigned int num; /* rendezend adatok db. szma */ unsigned int width; /* egy adat hossza */

203

int (*comp)(); fggvnyre Rendezi az hasznlja. adatokat a

/* int-tel visszatr - hasonlt mutat pointer memriban. A */ QUICK-short algoritmust

Vissza: semmi. Hasonlt fggvny visszatrsi rtkei: <0 -> elem1 < elem2 0 -> elem1 = elem2 >0 -> elem1 > elem2

21. plda / C2P21.C qsort()-tal /

parancssor

paramterek

rendezse

22. plda / C2P22.C - egsz szmok rendezse qsort()-tal /

92. 93.

char *lsearch(key,base,num,width,comp) char *lfind(key,base,num,width,comp) char *key; /* keresend kulcs char *base; /* adathalmaz cme unsigned int num; /* adatok darabszma unsigned int width; /* egy adat hossza int (*comp)(); /* pointer a hasonlt fggvnyre

*/ */ */ */ */

Mindkt fggvny lineris keresst valst meg base adathalmazban gy, hogy key kulcsot keresi. Hasonlt fggvny visszatrsi rtkei: 0, akkor azonos az aktulis elem a keresend kulccsal (meg van!) nem 0, akkor nem azonos Sok kzl az elst adja vissza, ha nincs: NULL pointert ad. Klmbsg: ha nincs a keresett kulcs, akkor a lsearch() fggvny az adathalmaz vgre beilleszti a keresett kulcsot, s mdostja a num pointer ltal mutatott db. szm rtket. 94. char *bsearch(key,base,num,width,comp) char *key; /* keresend kulcs char *base; /* adathalmaz cme unsigned int num; /* adatok darabszma unsigned int width; /* egy adat hossza int (*comp)(); /* pointer a hasonlt fggvnyre meg a key kulcsot. Az

*/ */ */ */ */ adatoknak

Binris keresssel keresi rendezettnek kell lennie. <0 -> elem1 < elem2 0 -> elem1 = elem2 >0 -> elem1 > elem2

Hasonlt (comp) rutin visszatrsi rtkei:

204

23. 24.

plda / C2P23.C - long tmbben adat keresse lfind()-dal / plda / C2P24.C - long tmbben adat keresse bsearch()-cal /

Rendszerkzeli programozs MS-DOS-ban: -------------------------------------Ez a leginkbb gpfgg rsze a C-nek. Kzvetlen ROM-BIOS fggvnyek illetve DOS interruptok hvsa Regiszterkonvencis paramtertads Alap: dos.h; struct itt vannak a regiszterek is definilva. WORDREGS {

unsigned int ax; unsigned int bx; unsigned int cx; unsigned int dx; unsigned int si; unsigned int di; unsigned int cflag; } struct BYTEREGS {

unsigned char al,ah; unsigned char bl,bh; unsigned char cl,ch; unsigned char dl,dh; } union REGS { struct WORDREGS x; struct BYTEREGS h; } Elfordulhat, hogy az ltalnos regiszterek mellett a szegmens regisztereket is mdostani kell. Emiatt van deklarlva az albbi struktra: struct SREGS { unsigned int es; unsigned int cs; unsigned int ss; unsigned int ds; } Lssunk nhny fggvnyt: ------------------------95. int int86(intn,inregs,outregs) int intn; /* az interrupt sorszma */ union REGS *inregs; /* bemen regiszterek */ union REGS *outregs; /* kimen regiszterek */

Vgrehajt egy 8086-os szoftver megszaktst, amelyet az intn hatroz meg. A megfelel regisztereket be kell lltani; hibakdokat a megfelelekben adja vissza. Ezeket az aktulis DOS verzi kziknyvbl kell kinzni. Vissza: AX tartalma hibakd _doserrno (stdlib.h-ban) szintn hibakd

205

96. int intdos(inregs,outregs) union REGS *inregs,*outregs;

dekl.: dos.h

A DOS 0x21-es rendszerhvst aktivizl, a belltott input regisztereknek megfelelen. Az inregs.h.ah-ba a DOS funkci sorszma tltend. Vissza: AX tartalma hibakd _doserrno (stdlib.h-ban) szintn hibakd 97. int int86x(intn,inregs,outregs,segregs) int intn; union REGS *inregs,*outregs; union SREGS *segregs; /* szegmens hvskor */ Hasonl az int86()-hoz, szegmensregiszterek rtkt. de llthatjuk

regiszterek az ES

rtke s DS

Visszatrskor visszatlti DS eredeti rtkt s lltja cflag-et az outregs-ben. Vissza: AX regiszter _doserrno-ban hibakd. 98. int intdosx(inregs,outregs,segregs) union REGS *inregs,*outregs; union SREGS *segregs; DOS rendszerhvs hasonlan intdos()-hoz, de DS s ES regiszterek is bellthatk. Egyebekben teljesen megegyeznek. rtke. Ha cflag nem egyenl 0-val, akkor

25. 26.

plda / C2P25.C - interrupt kezels - kpernytrls / plda / C2P26.C lekrdezse / interrupt kezels a DOS rjnak

Van mg nlkl. Ezek:

nhny

lehetsg

DOS

funkcik

elrsre,

de

interrupt

_dos ....fggvnyek: _dosallocmem _dos_close _dos_creat /0x3C/ _dos_creatnew /0x5B/ _dos_finfirst _dos_findnext _dos_freemem _dos_getdate _dos_getdiskfree _dos_getdrive _dos_getfileattr _dos_getftime _dos_gettime _dos_getvect _dos_keep(!) _dos_open _dos_setdate _dos_setdrive stb...

206

27. _

plda / C2P27.C file-ok keresse /

fggvnyargumentumoknak lennik. Ha message-et char *message;

207

You might also like