| 6. FEJEZET | Tartalom | 8. FEJEZET |
7. FEJEZET:
Adatbevitel és adatkivitel
Az adatbeviteli és adatkiviteli szolgáltatás nem része a C nyelvnek, így idáig nem fordítottunk rá nagy figyelmet. Nyilvánvaló viszont, hogy a programok a környezettel a korábban bemutatottnál sokkal bonyolultabb kapcsolatban is lehetnek. Ezen kapcsolat kialakítása érdekében ebben a fejezetben leírjuk a standard könyvtárat, ami függvények gyűjteménye. Ezek a függvények a C nyelvű programok adatbeviteli és adatkiviteli, karaktersorozat-kezelési, tárkezelési, matematikai műveletekkel kapcsolatos és más egyéb szolgáltatásait látják el.Az ANSI szabvány ezeket a könyvtári funkciókat pontosan definiálja, így ezek bármely C nyelvet használó számítógép és operációs rendszer számára kompatibilis formában léteznek. Azok a programok, amelyek az operációs rendszerrel való kölcsönhatásukat a standard könyvtáron keresztül bonyolítják, minden változtatás nélkül átvihetők az egyik számítógépről a másikra.
A könyvtári függvények tulajdonságait több mint egy tucat header állomány specifikálja, amelyek közül néhánnyal (<stdio.h>,<string.h>,<ctype.h>) már találkoztunk. Most nem a teljes könyvtárat fogjuk ismertetni, hanem számos érdekes C nyelvű programot írunk a könyvtári függvények felhasználásával. Magának a könyvtárnak a részletesebb leírása aB. Függelékbentalálható.
7.1. A standard adatbevitel és adatkivitel
Amint azt az1. fejezetbenmár elmondtuk, a könyvtár adatátvitelt kezelő része a szöveges adatbevitelre és adatkivitelre kialakított egyszerű modell alapján működik. A modell szerint a szövegáram egymást követő sorokból áll és minden sor egy újsorkarakterrel zárul. Ha a rendszer nem működik másképpen, akkor a könyvtári eljárás feladata annak eldöntése, hogy mi a teendő az újsor-karakter megjelenésekor. Például a könyvtári eljárás a bemenetről érkező kocsivissza- és soremelés-karaktereket egy újsor-karakterré alakítja, majd kiírás esetén elvégzi a visszaalakítást.A legegyszerűbb adatbeviteli mechanizmus, hogy egy időben egy karaktert olvasunk be astandard bemeneti eszközről(szokásos módon a billentyűzetről) agetcharfüggvénnyel:
int getchar(void)
Agetcharfüggvény minden hívásakor visszatér a következő karakterrel vagy azEOFjellel, ha az állomány végét érzékelte. AzEOFszimbolikus állandó az<stdio.h>headerben van definiálva. AzEOFértéke általában -1, de az ellenőrzéseknél inkább a szimbolikus állandót használjuk, hogy a program függetlenné váljék az adott számértéktől.A legtöbb környezetben egy állomány helyettesítheti a billentyűzetet a megállapodás szerinti<átirányítási jelet használva. Ha aprogprogram agetcharfüggvényt használja, akkor a
prog <allomanyban
parancssor hatására a rendszer a karaktereket azallomanybannevű adatállományból fogja beolvasni. A bemeneti eszköz átkapcsolása úgy történik, hogy aprogmaga nem érzékeli a változást, az „<allomanyban” karaktersorozat nem kerül be azargvparancssor-argumentumba. A bemenet átirányítása szintén nem érzékelhető aprogszámára, ha a bemeneti adatait egy másik programtól az ún pipeing mechanizmussal (láncolással) kapja. Sok rendszerben ez a
masprog | prog
parancssorral kérhető. Ennek hatására amasprogstandard kimenete, mintegy csővezetéken keresztül rákapcsolódik a prog bemenetére. A standard kimenetet az
int putchar(int)
függvény állítja elő. Aputchar(c)ackaraktert adja a standard kimeneti eszközre, ami alapfeltételezés szerint a képernyő. Aputcharfüggvény a hívása után a kiírt karakterrel vagy ha valamilyen hiba fordult elő, akkor azEOFjelzéssel tér vissza. A standard kimenet is átirányítható egy adatállományba a>állománynévparancskiegészítéssel. Ha aproghasználja aputcharfüggvényt, akkor az átirányítás a
prog >kiallomany
parancssorral történhet, és ennek hatására a standard kimenet helyett akiallomanynevű adatállományba íródik a kimenet. A pipeing mechanizmus szintén megvalósítható a
prog | masprog
parancssorral, ami aprogstandard kimenetét amasprogstandard bemenetére irányítja.Aprintffüggvény szintén a standard kimenetet használja. Aputcharésprintffüggvények hívásai vegyesen is előfordulhatnak és a kimenet a hívások sorrendjében jön létre.
Minden forrásállományban, amely hivatkozik az adatbeviteli-adatkiviteli könyvtár függvényeire, kell hogy szerepeljen az
#include <stdio.h>
sor az első függvényhivatkozás előtt. Amikor a rendszer megtalálja a hegyes zárójelek között a header nevét, akkor azt standard könyvtárban (pl. UNIX operációs rendszer esetén az/usr/includekönyvtárban) kezdi keresni.Nagyon sok program csak egyetlen bemeneti adatáramot használ és csak egyetlen kimeneti adatáramot hoz létre. Az ilyen programok adatbeviteli és adatkiviteli feladatainak ellátására agetchar,putcharésprintffüggvények teljesen elegendőek, és ez az induláshoz szintén elegendő. Ez különösen igaz, ha kihasználjuk az átirányítás lehetőségét, amellyel az egyik program kimenetét a másik bemenetéhez kapcsoljuk. Példaként vizsgáljuk meg a karakteres adatbevitelt és adatkivitelt használólowerprogramot, amely a bemenetére adott szöveget kisbetűs szöveggé alakítja.
#include <stdio.h> #include <ctype.h> main() /* a bemenetet kisbetűssé alakítja */ { int c; while ( (c = getchar()) != EOF) putchar(tolower(c)); return 0; }
Atolowerfüggvény a<ctype.h>headerben van definiálva és a nagybetűket kisbetűkké alakítja, más karakterekkel érintetlenül visszatér a hívó függvénybe. Mint korábban már említettük, az<stdio.h>getcharésputchar, valamint a<ctype.h>tolower„függvényei” gyakran makróként vannak megvalósítva, mivel így az egy karakterre eső műveletszám (és így a futási idő is) csökkenthető. Azt, hogy ez hogyan valósítható meg, a8.5. pontbanfogjuk bemutatni. Függetlenül attól, hogy egy adott gépen ezek a függvények hogyan vannak megvalósítva, a program használja azokat és semmiféle ismerettel nem kell rendelkeznie a karakterkészletről.
7.1.gyakorlat. Írjunk programot, amely a hívásakor azargv[0]-ban elhelyezett paramétertől függően a nagybetűket kisbetűvé vagy a kisbetűket nagybetűvé alakítja!
7.2. A formátumozott adatkivitel – a printf függvény
Aprintfkimeneti függvény a gépen belüli értékeket karakterekké alakítja. Aprintffüggvényt már a korábbi fejezetekben is használtuk és ott megadtuk a tipikus alkalmazásokra vonatkozó, de korántsem teljes leírását. A teljes leírás aB. Függelékbentalálható. A függvény alakja:
int printf(char *format, arg1, arg2, ...)
Aprintffüggvény aformatvezérlése alatt konvertálja, formátumozza és a standard kimenetre írja az argumentumai értékét, majd visszatéréskor megadja a kiírt karakterek számát.Aformatkaraktersorozata kétféle objektumot tartalmaz: közönséges (nyomtatható) karaktereket, amelyek közvetlenül átmásolódnak a kimeneti adatáramba és konverziós specifikációkat, amelyek mindegyike aprintfsoron következő argumentumának konverzióját és kiírását eredményezi. Mindegyik konverziós specifikáció a%jellel kezdődik és egy konverziós karakterrel végződik. A%jel és a konverziós karakter között sorrendben a következők lehetnek:
- Egy mínusz jel, ami a konvertált argumentum balra igazítását írja elő.
- Egy szám, ami megadja a minimális mezőszélességet. A konvertált argumentum legalább ilyen széles mezőbe fog kiíródni. Ha szükséges, akkor a mező bal széle (vagy ha balra igazítást írtunk elő, akkor a jobb széle) üres helyekkel fog feltöltődni.
- Egy pont, ami elválasztja a mezőszélességet a pontosságot megadó számtól.
- Egy szám (pontosság), ami megadja egy karaktersorozatból kiírt karakterek maximális számát, vagy lebegőpontos adat kiírásánál a tizedespont után kiírt számjegyek számát, vagy egész típusú adat esetén a kiírt számjegyek minimális számát.
- Egy h betű, ha egy egész számotshorttípusúként vagy egy l betű, halongtípusúként írunk ki.
A konverziós karaktereket a 7.1. táblázat tartalmazza. Ha a % jel utáni karakter nem konverziós specifikáció akkor aprintfviselkedése definiálatlan.
| A konverziós karakter | Az argumentum típusa | A nyomtatás módja |
| d, i | int | decimális szám |
| o | int | előjel nélküli oktális szám vezető nullák nélkül) |
| x, X | int | előjel nélküli hexadecimális szám (a vezető0xvagy0Xnélkül), a10…15jelzése azabcdefvagyABCDEFkarakterekkel |
| u | int | előjel nélküli decimális szám |
| c | int | egyetlen karakter |
| s | char* | karaktersorozatból karaktereket nyomtat a ‘\0‘ végjelzésig vagy a pontossággal megadott darabszámig |
| f | double | [-]m.ddddddalakú decimális szám, aholdszámjegyeinek számát a pontosság adja meg (alapfeltételezés szerintd=6) |
| e, E | double | [-]m.ddddddexx vagy[-]m.ddddddE xxalakú decimális szám, aholdszámjegyeinek számát a pontosság adja meg (alapfeltételezés szerintd=6 |
| g, G | double | %evagy%Ealakú kiírást használ, ha a kitevő< -4vagy>=pontosság, különben a%falakú kiírást használja. A tizedespont és az utána következő értéktelen nullák nem íródnak ki |
| p | void* | mutató a géptől függő kiírási formában |
| n | int* | aprintffüggvény aktuális hívásakor kiírt karakterek száma beíródik az argumentumba. Az argumentum nem konvertálódik |
| % | nincs konvertálandó argumentum | egy%jelet ír ki |
A szélesség vagy pontosság a*jellel is megadható, és ebben az esetben az érték a következő argumentum (amely kötelezőeninttípusú kell, hogy legyen) konverziójakor jön létre. Például ha azskaraktersorozatból legfeljebbmaxszámú karaktert akarunk kiírni, akkor ez a
printf("%.*s", max, s);
utasítással érhető el.A formátumkonverziók többségét a korábbi fejezetekben már bemutattuk, az egyetlen kivételt a karaktersorozatok kinyomtatásánál megadott pontosság hatásának elemzése képezi. A következő példasoron bemutatjuk a pontosság előírásának hatását a„Halló mindenki!”15 karakteres karaktersorozat kiírására. Az egyes mezők két szélén kettőspontokat íratunk ki, hogy jobban kiemeljük a mezőt.
:%s: :Halló mindenki!: :%10s: :Halló mindenki!: :%.10s: :Halló mind: :%-10s: :Halló mindenki!: :%.20s: :Halló mindenki!: :%-18s: :Halló mindenki! : :%18.10s: : Halló mind: :%-18.10s: :Halló mind :
Figyelem! Aprintffüggvény az első argumentumát használja annak eldöntésére, hogy még hány argumentum következik és azoknak mi a típusa. Programhiba keletkezik és hibás kiírást kapunk, ha nincs elegendő argumentum vagy azok nem a megfelelő típusúak. Ezt jól szemlélteti az alábbi kétprintfhívás összehasonlítása.
printf(s); /* hibás, ha s % jelet is tartalmaz */ printf("%s", s); /* ez így biztonságos */
Asprintffüggvény ugyanazt a konverziót hajtja végre, mint aprintf, de a kimenetet egy karaktersorozatban tárolja. A függvény:
int sprintf(char *string, char *format, arg1, arg2, ...)
Asprintffüggvény először aformatformátummegadás szerint formatálja azarg1,arg2stb. argumentumokat, majd az eredményt a standard kimenet helyett astringkaraktersorozatba helyezi. A string karaktersorozatnak elegendően nagynak kell lenni ahhoz, hogy az eredményt tárolni tudja. Azsprintffüggvényt főleg arra használhatjuk, hogy részekből egy átmeneti tárolóban állítsuk össze a kiírandó információt, majd amikor minden rész a rendelkezésünkre áll, akkor ezt az átmeneti változót aprintffüggvénnyel kiíratjuk.
7.2.gyakorlat. Írjunk programot, amely a tetszőleges bemeneti szöveget értelmes módon írja ki! A minimális igény, hogy a nem nyomtatható karaktereket a helyi szokásoknak megfelelően oktális vagy hexadecimális számként írjuk ki és a túl hosszú sorokat tördeljük rövidebb sorokra!
7.3. A változó hosszúságú argumentumlisták kezelése
Ebben a pontban megírjuk aprintffüggvény minimális igényeket kielégítő változatát, hogy bemutassuk, hogyan lehet olyan hordozható függvényeket írni, amelyek képesek egy változó hosszúságú argumentumlista feldolgozására. Mivel főleg az argumentumok feldolgozására helyeztük a hangsúlyt, az általunk írtminprintffüggvény csak a formátumot leíró karaktersorozatot és az argumentumokat dolgozza fel, a formátumkonverziót a valódiprintffüggvény hívásával valósítja meg. Aprintfdeklarációja:
int printf(char *fmt, ...)
ahol a…azt jelzi, hogy az argumentumok száma és típusa változhat. A deklarációban a…csak az argumentumlista végén jelenhet meg. Az általunk írtminprintffüggvény deklarációja
void minprintf(char *fmt, ...)
mivel mi nem adjuk vissza a kiírt karakterek számát, mint aprintf.Egy kicsit „trükkös”, ahogyan aminprintfvégigmegy az argumentumlistán, miközben az egyetlen nevet sem tartalmaz. A<stdarg.h>headerben néhány makródefiníció található, amelyek lehetővé teszik az argumentumlistán való lépkedést. Ennek a headernek a konkrét kialakítása számítógéptől függ, de a makrók szoftver-interfésze egységes.
Ava_listtípust fogjuk használni a sorjában következő egyes argumentumokra hivatkozó változók deklarálására. Aminprintffüggvényben ezt a változótam-nek nevezzük, az „argumentummutató” kifejezés alapján. Ava_startmakró úgy inicializálja azamváltozót, hogy az az első név nélküli argumentumra mutasson. Ezértamhasználata előtt ava_startmakrót egyszer végre kell hajtatni. A listában legalább egy névvel ellátott argumentumnak kell lenni és ezt az utolsó, névvel ellátott argumentumot használja ava_starta meg nem nevezett argumentumok listáján való elinduláshoz.
Ava_argminden egyes hívása után egy argumentummal tér vissza és azammutatót tovább lépteti a következő argumentumra. Ava_argvisszatérésekor az argumentum értéke olyan típusú lesz, mint amit hívásakor megadtunk. Az argumentumlista feldolgozási folyamatát aminprintffüggvényt záróreturnutasítás kiadása előtt ava_endmakró hívásával kell lezárni. (Az argumentumlistát feldolgozó makrók leírása aB. Függelék 7. pontjábantalálható.)
Ezekkel a jellemzőkkel kialakítottminprintffüggvény:
#include <stdio.h> #include <stdarg.h> #include <ctype.h> /* minprintf: változó hosszúságú argumentumlistát kezelő, egyszerűsített printf függvény */ void minprintf(char *fmt, ...) { va_list am; /* sorjában az egyes név nélküli argumentumokra mutat */ char *p, *sert; int iert; double dert; va_start(am, fmt); /* hatására am az első név nélküli argumentumra fog mutatni */ for (p = fmt; *p; p++) { if (*p != '%') { putchar(*p); continue; } switch (*++p) { case 'd': iert = va_arg(am, int); printf("%d", iert); break; case 'f': dert = va_arg(am, double); printf ("%f", dert); break; case 's': for (sert = va_arg(am, char *); *sert; sert++) putchar(*sert); break; default: putchar(*p); break; } } va_end(am); /* a listafeldolgozás lezárása */ }
7.3.gyakorlat. Egészítsük ki aminprintfprogramot újabb,printffüggvényben megengedett lehetőségekkel!
7.4. Formátumozott adatbevitel – a scanf függvény
Az adatbevitelt végzőscanffüggvény aprintfanalógiája, és többségében azonos (de ellentétes irányú) adatkonverziókat képes elvégezni. Általános alakja:
int scanf(char *format, ...)
Ascanffüggvény a standard bemenetről karaktereket olvas és aformat-ban megadott specifikációk szerint értelmezi azokat, majd az eredményt eltárolja a további argumentumokban. A formátumot leíró argumentumot hamarosan részleteiben is tárgyaljuk, a többi argumentumnak viszont kötelezően mutatónak kell lenni, ami arra a helyre mutat, ahová a konvertált értéket helyezzük. Csakúgy, ahogyan aprintfesetén, itt is csak ascanflegfontosabb jellemzőit írjuk le és nem megyünk bele a részletekbe.Ascanffüggvény működése befejeződik, ha feldolgozta a formátumot leíró karaktersorozatot vagy hibát érzékel (az aktuális bemenet nem illeszkedik a vezérlő specifikációhoz). A függvény visszatérési értéke a sikeresen illesztett (konvertált) és argumentumokban elhelyezett adatok száma. Ebből eldönthető, hogy ascanfhány bemeneti tételt talált. Az adatállomány végének elérésekor a visszaadott értékEOF. Ügyeljünk arra, hogy ez egy nullától különböző érték és azt jelenti, hogy a következő bemeneti karakter nem illeszkedik a formátumot leíró karaktersorozat első specifikációjához! Ha ascanffüggvényt többször egymás után hívjuk, akkor a következő hívás esetén a keresés közvetlenül az utoljára visszaadott és már konvertált karakter után folytatódik.
Ascanffüggvénynek létezik egysscanfváltozata, ami asprintf-hez hasonló és a standard bemenet helyett egy karaktersorozatból olvas:
int sscanf(char *string, char *format, arg1, arg2, ...)
Asscanffüggvény aformat-ban megadott formátum szerint kiolvassa astringkaraktersorozatot és a konvertált értékeket elhelyezi azarg1,arg2stb. argumentumokban, Természetesen ezek az argumentumok is mutatók.A formátumot leíró karaktersorozat konverziós specifikációkat tartalmaz, amelyeket a bemenet átalakításának vezérlésére használunk. A formátumvezérlő karaktersorozat a következő karaktereket tartalmazhatja:
- Szóközök és tabulátorkarakterek, amelyeket ascanfnem vesz figyelembe.
- Közönséges (%jeltől különböző) karakterek, amelyek várhatóan illeszkednek a bemeneti adatáram következő nem üres karakteréhez.
- Konverziós specifikáció, ami a%jelből, az opcionális hozzárendelés-elnyomó*karakterből, a maximális mezőszélességet előíró opcionális számból, egy opcionálish,lvagyLbetűből (amelyek a célmező szélességét jelzik), valamint egy konverziós karakterből áll.
A konverziós specifikáció irányítja a következő bemeneti mező átalakítását. Normális esetben az eredmény a megfelelő argumentummal (mint mutatóval) címzett vátlozóba kerül. Ha a hozzárendelés-elnyomó*karaktert alkalmaztuk, akkor ascanfa bemeneti mezőt átlépi és nem történik meg az érték változóhoz rendelése. Egy bemeneti mező alatt a nem üres karakterekből álló karaktersorozatot értjük, ami a következő üres karakterig tart, vagy addig, amíg a mezőszélesség (ha megadtuk) el nem fogy. Ebből következik, hogy ascanfátmegy a sorhatárokon is a bemeneti adat keresése közben, mivel az újsor-karakter is üres karakternek számít. (Üres karakter a szóköz, a tabulátor, a kocsi-vissza, a soremelés, a függőleges tabulátor és a lapdobás.)
A konverziós karakter adja meg a bemeneti mező értelmezését. A neki megfelelő argumentumnak mutatónak kell lenni, ahogyan ezt a C nyelv érték szerinti hívása megköveteli. A konverziós karaktereket a 7.2. táblázat tartalmazza.
Ad,i,o,uésxkonverziós karakterek előtt állóhazt jelzi, hogy a mutató az argumentumlistában szereplőinttípusshortváltozatára mutat, és hasonlóan azlalongváltozatra utal. Aze,fésgkonverziós karakterek előtt megjelenőlarra utal, hogy afloattípusú argumentumdoubleváltozatú.
| A konverziós karakter | Az argumentum típusa | A beolvasott adat |
| d | int* | decimális egész |
| i | int* | egész szám, ami lehet oktális (vezető nullákkal) vagy hexadecimális (vezető0xvagy0Xkarakterekkel) |
| o | int* | oktális egész szám (vezető nullákkal vagy azok nélkül) |
| u | unsignedint* | előjel nélküli decimális egész szám |
| x | int* | hexadecimális egész szám (a vezető0x, ill.0Xkarakterekkel vagy azok nélkül) |
| c | char* | karakterek. A következő bemeneti karakterek (alapfeltételezés szerint 1) elhelyezése a kijelölt mezőben. Az üres helyek átlépését (mint normális esetet) elnyomja, ha a következő nem üres karaktert akarjuk beolvastatni, akkor a%1sspecifikációt kell használni |
| s | char* | karaktersorozat (aposztrófok nélkül). Achar* mutató egy elegendően nagy karaktersorozatra mutat és a záró ‘\0‘ jelzést a beolvasás után automatikusan elhelyezi |
| e, f, g | float* | lebegőpontos szám, opcionális előjellel opcionális tizedesponttal és opcionális kitevővel |
| p | void* | mutató, olyan formában, ahogyan azt aprintf(„%p”)kiírta |
| n | int* | az aktuálisscanfhívással beolvasott karakterek száma beíródik az argumentumba. Nem történik adatbeolvasás, a konvertált tételek száma nem nő |
| […] | char* | a bemeneti karakteráramból beolvassa a zárójelek közötti karakterekkel (illeszkedési halmazzal) megegyező karakterekből álló leghosszabb nem üres karaktersorozatot és lezárja a‘\0‘végjellel. A[]…]formában megadott halmaz esetén a]karakter a halmaz része lesz |
| [^…] | char* | az illeszkedési halmazzal nem megegyező karakterekből álló karaktersorozat beolvasása és‘\0‘végjellel történő lezárása. A[^]…]formában megadott halmaz esetén a]karakter a halmaz része lesz |
| % | nincs hozzárendelés | %jel mint karakteres állandó |
Ascanfhasználatának bemutatását kezdjük a4. fejezetbenismertetett egyszerű kalkulátor programjának módosításával! A programban a bemeneti adatok átalakítását oldjuk meg ascanffüggvénnyel.
#include <stdio.h> main() /* egyszerű kalkulátor */ { double sum, v; sum = 0; while (scanf("%lf", &v) == 1) printf ("\t%.2f\n", sum += v); return 0; }
A következő példában tegyük fel, hogy
1994 december 25
alakban írt dátumot akarunk beolvasni. Ez ascanffüggvénnyel
int nap, ev; char honapnev[20]; scanf("%d %s %d", &ev, honapnev, &nap);
formában valósítható meg a beolvasás. Vegyük észre, hogy ahonapnevelőtt nem írtuk ki az&jelet, mivel egy tömbnév mindig mutató!Ascanfformátumot leíró karaktersorozatában karakteres állandók (literálisok) is megjelenhetnek, de azoknak illeszkedni kell a bemenetről érkező ugyanolyan karakterekhez. Ha pl. a dátumot éé/hh/nn alakban akarjuk beolvastatni ascanffüggvénnyel, akkor az
int nap, honap, ev; scanf("%d/%d/%d", &ev, &honap, &nap);
formátumleírást kell alkalmazni.Ascanffüggvény beolvasáskor kiszűri a formátumot megadó karaktersorozatban lévő szóközöket és tabulátorokat, sőt átlépi az üreshely-karaktereket (szóköz, tabulátor, új sor stb.) a beolvasott adatáramban is a beolvasandó érték keresése közben. Ha a bemeneti adatáram nem rögzített formátumú, akkor célszerű egyszerre egy egész adatsort beolvasni és azsscanffüggvénnyel részleteiben elővenni és feldolgozni. Ha például olyan sort akarunk beolvasni, ami a korábbi két dátumírási mód bármelyikével írt dátumot tartalmaz, akkor ezt úgy tehetjük meg, hogy agetlinefüggvénnyel beolvassuk a teljes sort, majd azsscanffüggvénnyel feldolgozzuk.
while (getline(sor, sizeof(sor)) > 0) { if (sscanf (sor, "%d %s %d", &ev, honapnev, &nap) == 3) printf ("érvényes: %s\n", sor); /* 1994 december 25 alakú dátum */ else if (sscanf(sor, "%d/%d/%d", &ev, &honap, &nap) == 3) printf("érvényes: %s\n", sor); /* éé/hh/nn alakú dátum */ else printf("érvénytelen: %s\n", sor); /* érvénytelen alakú dátum */ }
Ascanfés más adatbeviteli függvények hívásai egymással keverhetők. Bármelyik adatbeviteli függvény következő hívásakor a beolvasás az első,scanfáltal már be nem olvasott karakterrel kezdődik.Befejezésül még egyszer kihangsúlyozzuk, hogy ascanféssscanffüggvények argumentumainak mutatóknak kell lennie! Nagyon gyakori hiba, hogy
scanf ("%d", n); /* HIBÁS!!! */
alakban írjuk a függvényhívást a
scanf("%d", &n); /* Helyes! */
helyett. Ez a hiba általában nem derül ki a program fordítása közben.
7.4.gyakorlat. Írjuk meg ascanffüggvény egyszerűsített változatát az előző pontban látottminprintfmintájára!
7.5.gyakorlat. Írjuk meg a4. fejezetbenismertetett postfix adatbeírási formátumú kalkulátorprogram új változatát, amely a bemeneti adatok és számok átalakítását ascanfés/vagysscanffüggvénnyel valósítja meg!
7.5. Hozzáférés adatállományokhoz
Az eddigi példaprogramok mindegyike a standard bemenetről olvasta az adatokat és a standard kimenetre írta az eredményt. A standard bemenetet és kimenetet a helyi operációs rendszer automatikusan hozzárendeli a programhoz.A következőkben egy olyan programot mutatunk be, amely egy adatállományhoz fér hozzá, amitnemaz operációs rendszer rendelt átirányítással a programhoz. Az adatállományokhoz való hozzáférés szükségességét és megvalósítási lehetőségét illusztrálócatprogram megnevezett adatállományok halmazát gyűjti egybe (konkatenálja az állományokat) és az eredményt a standard kimenetre írja. Acatfő alkalmazási területe, hogy állományokat gyűjtsön egybe és nyomtassa ki azokat, vagy az önálló, név szerinti állomány-hozzáférésre nem felkészített programok bemeneti információit gyűjtse be. Például a
cat x.c y.c
parancs a standard kimenetre írja azx.césy.cnevű állományok (és csak ezen állományok) tartalmát.A program kialakításánál a fő kérdés, hogy hogyan érhetjük el a megnevezett állományok beolvasását, vagyis azt, hogy a felhasználó által szabadon választott külső állománynevet az adatbeolvasó utasításhoz rendeljük.
A szabály rendkívül egyszerű! Mielőtt az állományból olvasnánk vagy abba írnánk, az állományt afopenkönyvtári függvénnyel meg kell nyitni. Afopenveszi a külső állománynevet (mint pl.x.cvagyy.c), mindenféle belső adminisztrációt végez, felveszi a kapcsolatot az operációs rendszerrel (amelynek részleteivel nem kell törődnünk), majd egy mutatót ad vissza, ami ezt követően az állomány olvasásánál vagy írásánál használható.
Ez a mutató, amitállomány-mutatónak (fájlpointernek)neveznek, egy struktúrát címez, ami az állományra vonatkozó információkat (a puffer helye; az aktuális karakterpozíció a pufferban; annak jelzése, hogy az állományt írásra vagy olvasásra vesszük-e igénybe; a hiba vagy állományvég előfordulásakor szükséges teendők stb.) tartalmazza. A felhasználónak nem szükséges a részleteket ismerni, mivel a struktúraFILEnéven definiálva van az<stdio.h>standard headerben. Az állománykezeléshez csak az állománymutatót kell deklarálni a következő minta szerint:
FILE *fp; FILE *fopen(char *nev, char *mod);
A deklaráció azt mondja ki, hogyfpegyFILEtípusú struktúrához tartozó mutató és afopenegyFILEtípusú struktúrát címző mutatóval tér vissza. Vegyük észre, hogyFILEegy típusnév, mint pl. azint, és nem igazi struktúranév. AFILEtípusttypedefutasítással deklarálták a könyvtárban (egyébként afopenUNIX operációs rendszer alatti megvalósításának részleteit a8.5. pontbanírjuk le). Afopenfüggvény egy programból az
fp = fopen(nev, mod);
utasítással hívható. Afopenelső argumentuma egy karaktersorozat, amely a megnyitandó állomány nevét tartalmazza. A második argumentum szintén egy karaktersorozat, ami azt jelzi, hogy a felhasználó hogyan akarja használni az állományt (használati mód). A használati mód karaktersorozatában használható karakterek: olvasás („r”), írás („w”) és hozzáfűzés („a”, append). Néhány rendszer különbséget tesz szöveges és bináris állományok között, ilyenkor bináris állomány esetén a használati módot a„b”karakterrel kell kiegészíteni.Ha írásra vagy hozzáfűzésre egy nem létező állományt akarunk megnyitni, akkor az adott nevű állomány (ha lehetséges) létrejön. Egy létező állományt írásra megnyitva, annak korábbi tartalma elvész, hozzáfűzésre megnyitva viszont megmarad. Ha megpróbálunk olvasásra megnyitni egy nem létező állományt, akkor hibajelzést kapunk. Más hibaokok is előfordulhatnak, pl. ha olvasni akarunk egy létező, de számunkra nem hozzáférhető (nem engedélyezett) állományt. Ha bármilyen állománykezelési hiba fordul elő, akkor afopenNULLértékű mutatóval tér vissza. A hiba oka pontosabban is meghatározható, az erre alkalmas hibakezelő függvények leírása aB. Függelék 1. részébentalálható.
Acatprogram megírásához szükséges következő tudnivaló, hogy hogyan lehet a már megnyitott állományt olvasni vagy írni. Ennek számos lehetősége van, amelyek közül a legegyszerűbb agetcésputcfüggvények alkalmazásán alapszik. Agetcfüggvény az állományból kiolvasott következő karakterrel tér vissza, és hívásakor az állománymutató megadásával kell közölni, hogy melyik állományból olvasson. Általános alakja:
int getc(FILE *fp)
A függvény azfp-vel címzett adatáramból vett következő karakterrel vagy hiba eseténEOFjelzéssel tér vissza.Aputcadatkiviteli függvény
int putc(int c, FILE *fp)
alakú, és hívásakor a függvény azfp-vel címzett állományba kiírja ackaraktert, és visszatér a kiírt karakterrel vagy hiba esetén azEOFjellel. Hasonlóan agetcharésputchareljárásokhoz, gyakran agetcésputceljárásokat is makrók formájában írják meg, nem pedig függvényként, de ez a használatukat nem befolyásolja.Egy C nyelvű program indításakor az operációs rendszer három állományt mindig automatikusan megnyit és az ezekhez tartozó állománymutatókat a program rendelkezésére bocsátja. Ez a három állomány a standard bemenet, a standard kimenet és a standard hibaállomány, amelyek állománymutatói a<stdio.h>headerben vannak deklarálvastdin,stdoutésstderrnéven. Normális esetben azstdina billentyűzethez,stdouta képernyőhöz kapcsolódik, de ahogy azt a7.1. pontbanmár leírtuk, azstdinésstdoutminden további nélkül átirányítható adatállományokhoz vagy a pipeing mechanizmussal egy másik programhoz.
Agetc,putc,stdinésstdoutfelhasználásával agetcharésputchara következő módon definiálható:
#define getchar () getc(stdin) #define putchar(c) putc((c), stdout)
Adatállományok formátumozott olvasására vagy írására azfscanfésfprintffüggvényeket használhatjuk. Ezek használata lényegében megegyezik ascanfvagyprintffüggvények használatával, kivéve, hogy az első argumentumuknak az olvasandó vagy írandó állományt kijelölő állománymutatónak kell lennie és a formátumot leíró karaktersorozat a második argumentum. A függvények általános alakja:
int fscanf (FILE *fp, char *format, ...) int fprintf (FILE *fp, char *format, ...)
Ezen előzetes ismeretek birtokában most már hozzákezdhetünk az állományokat összekapcsolócatprogram írásához! A program felépítése megegyezik a korábbi programoknál már bevált felépítéssel: ha a programot parancssor-argumentummal hívjuk, akkor az argumentumok az állománynevek, ha nincs argumentum, akkor a standard bemenetről jövő adatok kerülnek feldolgozásra. A program:
#include <stdio.h> /* cat: állományok konkatenálása - 1. változat */ main(int argc, char *argv[]) { FILE *fp; void filecopy (FILE *, FILE *); if (argc ==1) /* nincs argumentum, a standard bemenetet másolja */ filecopy (stdin, stdout); else while (--argc > 0) if ((fp = fopen(*++argv, "r")) == NULL) { printf ("cat: nem nyitható meg %s\n", *argv); return 1; } else { filecopy (fp, stdout); fclose (fp); } return 0; } /* filecopy: az ifp-vel címzett állományt az ofp-vel cimzett állományba másolja */ void filecopy(FILE *ifp, FILE *ofp) { int c; while ((c = getc(ifp)) != EOF) putc (c, ofp); }
AzstdinésstdoutállománymutatókFILE* típusú objektumok. Azstdinésstdoutállandók, amelyeket a standard könyvtárban definiáltak, és nem pedig változók, így értéket sem rendelhetünk hozzájuk. Az
int fclose (FILE *fp)
függvény afopeninverze, és hatására megszakad a kapcsolat az állománymutató és a külső állománynév között, az állománymutató felszabadul és más állományhoz rendelhető. A legtöbb operációs rendszer korlátozza az egy program futása során egyidejűleg nyitott állományok számát, ezért amikor már nincs szükség rá, célszerű felszabadítani az állománymutatót, ahogy ezt acatprogramban is tettük. Azfclosemásik fontos feladata, hogy kimeneti állományok esetén kiírja az állományba aputcáltal használt puffer tartalmát. (Normális esetben a puffer tartalma csak akkor íródna ki, ha már a puffer megtelt. Az utolsó, általában nem egész puffernyi tartalmat azfclosefüggvénnyel kell kiíratni.) Afcloseautomatikusan végrehajtódik minden egyes megnyitott állományra, ha a program normálisan fut le. A standard bemenetet és kimenetet szintén lezárhatjuk, ha nincs szükségünk rájuk. Amennyiben újra szükségessé válik a használatuk, akkor afreopenkönyvtári függvénnyel nyithatók meg újra.
7.6. Hibakezelés – az stderr és exit függvények
Acatprogram hibakezelése messze nem ideális. A problémát az okozza, hogy ha egy állomány valamilyen hiba folytán hozzáférhetetlenné válik, a hibaüzenet a konkatenált kimeneti állomány (vagyis az eredmény) végére íródik ki. Ez megfelelő lehet, ha a kimeneti eszköz a képernyő, viszont elfogadhatatlan, ha egy állományba írunk vagy az eredményt a pipeing mechanizmussal egy másik programnak adjuk át.A hibák jobb kezelése érdekében a programhoz automatikusan egy második kimeneti állományt is hozzárendel az operációs rendszer (hasonlóan azstdinésstdoutállományokhoz), és ez azstderrhibaállomány. Azstderrállományba írt üzenetek normális esetben mindig a képernyőn jelennek meg, még akkor is, ha a standard kimenetet átirányítottuk.
Ennek alapján módosítsuk acatprogramot és a hibaüzenetet írassuk a standard hibaállományba!
#include <stdio.h> #include <stdlib.h> /* cat: állományok konkatenálása - 2. változat */ main (int argc, char *argv[]) { FILE *fp; void filecopy(FILE *, FILE *); char *prog = argv[0]; /* a program neve hiba esetén */ if (argc == 1) /* nincs parancssor-argumentum, a standard bemenetről másol */ filecopy(stdin, stdout); else while (--argc > 0) if ((fp = fopen(*++argv, "f")) == NULL) { fprintf(stderr, "%s: nem nyitható meg: %s\n", prog, *argv); exit (1); } else { filecopy(fp, stdout); fclose (fp); } if (ferror(stdout)) { fprintf(stderr, "%s: hiba stdout írásakor\n", prog); exit (2); } exit(0); }
A program a hibákat kétféle módon jelzi. Először is azfprintfáltal létrehozott hibaüzenetek azstderrállományba íródnak, tehát mindig a képernyőn jelennek meg, ahelyett, hogy a kimeneti (eredmény) állományba íródnának vagy a pipeing mechanizmussal egy másik program bemenetére kerülnének. A hibaüzenet kiírásába belefoglaltuk a programargv[0]-ból vett nevét, így ha a programot más programokkal együtt használjuk, azonosítható a hiba forrása.Azstderrállományon keresztüli hibajelzésen kívül a program használja azexitstandard könyvtári függvényt, ami hívásakor leállítja a program futását. Azexitargumentuma bármely, azexit-et hívó eljárás rendelkezésére áll, így a program sikeres vagy hibás végrehajtása egy másik, ezt a programot alprogramként használó eljárásból ellenőrizhető. Megállapodás szerint a0argumentum a program sikeres lefutását, a nem nulla argumentum pedig valamilyen, normálistól eltérő működését jelzi. Azexitfüggvény automatikusan hívja azfclosefüggvényt és minden egyes megnyitott kimeneti állományba kiírja a kimeneti pufferének tartalmát.
Amaineljárásban areturnkifejezésutasítás egyenértékű azexit(kifejezés) utasítással. Viszont azexitelőnye, hogy az más függvényekből is hívható és a hívás helye az5. fejezetbenleírt mintakereső programhoz hasonló programmal meghatározható.
Azferrorfüggvény nem nulla értékkel tér vissza, ha azfpállománymutatóval megcímzett állomány írása vagy olvasása közben hiba fordult elő. A függvény általános alakja:
int ferror(FILE *fp)
A gyakorlatban az adatkiviteli hibák ritkábban fordulnak elő, mint a beolvasási hibák (bár pl. hibát jelent, ha lemezre írás közben elfogy az üres hely), de a programot mindenképpen célszerű ellenőrizni.Afeof(FILE* ) függvény analóg aferrorfüggvénnyel és szintén nem nulla értékkel tér vissza, ha a megadott állományhoz való hozzáférés során hiba fordult elő. A függvény általános alakja:
int feof(FILE *fp)
A kis mintaprogramunk kapcsán általában nem érdemes túlzottan aggódni a kilépéskori állapotjelzés miatt, de nagyobb programok esetén gondot kell fordítani az értelmes és jól használható állapotjelzésekre.
7.7. Szövegsorok beolvasása és kiírása
A standard könyvtár tartalmazza azfgetsbemeneti függvényt, ami hasonló a korábbról már ismertgetlinefüggvényhez, és általános alakja:
char *fgets(char *sor, int maxsor, FILE *fp)
Azfgetsfüggvény azfpállománymutatóval megadott állományból beolvassa a következő bemenő sort (az újsor-karaktert is beleértve) és elhelyezi a sor nevű karakteres tömbben. A függvény legfeljebbmaxsor-1karaktert fog beolvasni és a beolvasott sort a‘\0‘jellel fogja lezárni. Normális esetben azfgetsa sor tömbbel tér vissza, de állomány vége vagy hiba esetén a visszatérési értékNULL. (A korábban használtgetlinefüggvény a sor hosszával tért vissza, ami nagyon hasznos volt, mivel így a nulla jelenthette az állomány végét.)Adatkiírásra azfputsfüggvény használható, amivel egy karaktersorozat (ami nem szükségszerű, hogy újsor-karaktert tartalmazzon) írható ki egy megadott állományba. A függvény általános alakja:
int fputs(char *sor, FILE *fp)
A függvényEOFjellel tér vissza, ha a kiíráskor hibát érzékel és nulla értékkel, ha minden rendben volt.A könyvtárigetsésputsfüggvények hasonlóak azfgetsésfputsfüggvényekhez, de mindig azstdinésstdoutállománymutatókkal kijelölt állományt kezelik. Használatuk során zavaró lehet, hogy agetstörli a billentyűzetről érkező ‘\n‘ újsor-karaktert, aputspedig mindig azzal zárja a kiírást.
Annak bizonyítására, hogy azfgetsvagyfputsfüggvényekben nincs semmi speciális, idemásoltuk a könyvtári függvények eredeti programkódját:
/* fgets: beolvas egy legfeljebb n karakteres sort az iop állománymutatóval kijelölt állományból */ char *fgets(char *s, int n, FILE *iop) { register int c; register char *cs; cs = s; while (--n > 0 && (c = getc(iop)) != EOF) if ((*cs++ = c) == '\n') break; *cs = '\n'; return (c == EOF && cs == s) ? NULL : s; } /* fputs: kiírja az s karaktersorozatot az iop állománymutatóval kijelölt állományba */ int fputs(char *s, FILE *iop) { int c; while (c = *s++) putc(c, iop); return ferror(iop) ? EOF : 0; }
A szabvány azt írja elő, hogy aferrorfüggvény nem nulla értékkel tér vissza, ha hiba volt, ezzel szemben azfputshiba eseténEOFjelzéssel, minden más esetben nem negatív értékkel tér vissza.Azfgetsfüggvény felhasználásával már egyszerűen megvalósíthatjuk agetlinefüggvényt:
/* getline: beolvas egy sort és visszatér a sor hosszával */ int getline(char *sor, int max) { if (fgets(sor, max, stdin) == NULL) return 0; else return strlen(sor); }
7.6.gyakorlat. Írjunk programot, amely összehasonlít két állományt, és kiírja az első olyan sort, ahol különböznek!
7.7.gyakorlat. Módosítsuk az5. fejezetbenismetetett mintakereső programot úgy, hogy bemenetét a parancssor-argumentumként megadott állománynevek listájából, vagy ha nincs argumentum, akkor a standard bemenetről vegye! Ki kell-e íratni az állomány nevét, ha a program egymáshoz illeszkedő sorokat talál?
7.8.gyakorlat. Írjunk programot, amely több állományt ír ki egymás után, minden állományt új oldalon kezdve! A program minden állomány kiírása előtt írja ki a lap tetejére a címet és az oldalakat állományonként folyamatosan számozza!
7.8. További könyvtári függvények
A standard könyvtárban számos, különböző feladatok megoldására alkalmas függvény található. Ebben a pontban néhány hasznos függvény rövid leírását adjuk. A részletes leírás, ill. a könyvtár további függvényeinek ismertetése aB. Függelékbentalálható.
7.8.1. Karaktersorozatokat kezelő függvények
Mint már korábban említettük, azstrlen,strcpy,strcat, ill.strcmpfüggvények deklarációja a<string.h>standard headerben található, a többi, karaktersorozatot kezelő függvény deklarációjával együtt. A következő leírásbanséstchar* típusú karaktersorozatot,césninttípusú adatot jelöl. Az egyes karaktersorozat-kezelő függvények:
| strcat(s , t) | atkaraktersorozatot azsvégéhez fűzi (konkatenálja); |
| strncat(s , t, n) | atkaraktersorozatndarab karakterét azsvégéhez fűzi; |
| strcmp(s, t) | negatív, nulla vagy pozitív értékkel tér vissza, has < t,s = tvagys > t; |
| strncmp(s, t, n) | ugyanúgy működik, mint azstrcmp, de az összehasonlítást csak az elsőnkarakterrel végzi el; |
| strcpy(s, t) | atkaraktersorozatots-be másolja; |
| strncpy(s, t, n) | atkaraktersorozat elsőnkarakteréts-be másolja; |
| strlen(s) | a visszatérési érték azskaraktersorozat hossza; |
| strchr(s, c) | visszatér ackarakterskaraktersorozatbeli első előfordulási helyének mutatójával, vagyNULLértékkel, hacnem fordul elős-ben; |
| strrchr(s, c) | visszatér ackarakterskaraktersorozatbeli utolsó előfordulási helyének mutatójával, vagyNULLértékkel, hacnem fordul elős-ben. |
7.8.2. Karaktervizsgáló és -átalakító függvények
A<ctype.h>standard header számos, karakterek vizsgálatára vagy átalakítására alkalmas függvényt tartalmaz. A leírt függvények visszatérési értékeinttípusú, a leírásukban szereplőcinttípusú, ésunsignedcharvagyEOFadatként értelmezhető.
| isalpha(c) | visszatérési érték nem nulla, hacalfabetikus karakter és nulla, ha nem az; |
| isupper(c) | visszatérési érték nem nulla, hacnagybetűs karakter és nulla, ha nem az; |
| islower(c) | visszatérési érték nem nulla, hackisbetűs karakter és nulla, ha nem az; |
| isdigit(c) | visszatérési érték nem nulla, hacszámjegykarakter és nulla, ha nem az; |
| isalnum(c) | visszatérési érték nem nulla, hacalfabetikus vagy számjegykarakter és nulla, ha nem az; |
| isspace(c) | visszatérési érték nem nulla, hacszóköz-, tabulátor-, újsor-, kocsivissza-, lapemelés- vagy függőlegestabulátor-karakter és nulla, ha nem az; |
| toupper(c) | visszatérési értékcnagybetűssé alakított értéke; |
| tolower(c) | visszatérési értékckisbetűssé alakított értéke. |
7.8.3. Az ungetc függvény
A standard könyvtárban megtalálható a4. fejezetbenmegírtungetchfüggvény egy szűkített változata, azungetc. A függvény általános alakja:
int ungetc (int c, FILE *fp)
Azungetcfüggvény ackaraktert visszahelyezi azfpállománymutatóval kiválasztott állományba, és visszatér magával acértékkel, vagy hiba esetén azEOFjelzéssel. Állományonként csak egy karakter visszahelyezése megengedett. Azungetcfüggvény mindenscanf,getcvagygetcharjellegű bemeneti függvénnyel használható.
7.8.4. Parancsvégrehajtás
Asystem(char*s)függvény végrehajtja azskaraktersorozatban elhelyezett parancsot, ha az éppen futó programban rá kerül a vezérlés. Azskaraktersorozat megengedett tartalma (a megengedett parancsok halmaza) nagymértékben függ a használt operációs rendszertől. Asystemfüggvény alkalmazására jó példa a UNIX operációs rendszer esetén kiadott
system ("date");
utasítás, ami a rendszerdateparancsának végrehajtását, azaz a dátum és a napi idő standard kimeneten való kiírását idézi elő. Asystema használt operációs rendszertől függő egész állapotjelzéssel tér vissza a végrehajtott parancsból. UNIX operációs rendszer esetén asystemvisszatérési állapotjelzése megegyezik azexitvisszatérési értékével.
7.8.5. Tárkezelő függvények
A tárolóban adott méretű terület dinamikus lefoglalása amallocéscallocfüggvényekkel lehetséges. Amallocfüggvény általános alakja:
void *malloc (size_t n)
A függvény egynbájtos, inicializálatlan tárterületet címző mutatóval, vagy ha a helyfoglalási igény nem elégíthető ki, aNULLértékű mutatóval tér vissza. A
void *calloc(size_t n, size_t meret)
általános alakúcallocfüggvényndarab megadott méretű objektum számára elegendő helyet biztosító tömb mutatójával, vagy ha a helyigény nem elégíthető ki, akkorNULLértékű mutatóval tér vissza.Amallocvagycallocfüggvények visszatérési értékeként kapott mutató a megadott objektumnak megfelelő helyre mutat, de kényszerített típuskonverzióval a kívánt típusúvá kell alakítani, pl. az
int *ip; ip = (int *) calloc (n, sizeof(int));
módon.Afree(p)függvény felszabadítja apmutatóval megcímzett helyet, aholpegy eredendőenmallocvagycallochívásával kapott mutató. Arra vonatkozóan, hogy melyik helyet szabadítjuk fel, nincs semmiféle megszorítás, de fatális hiba a következménye, ha nem amallocvagycallocfüggvény hívásával lefoglalt helyet akarunk felszabadítani.
Szintén programhibát okoz, ha a hely felszabadítása után akarjuk használni az adott hely tartalmát. Az alábbi, egy lista helyeit felszabadító ciklus tipikus, de inkorrekt programot ad:
for(p = fej; p != NULL; p = p->kovetkezo) /* HIBÁS! */ free(p);
A feladatot helyesen csak úgy tudjuk megoldani, ha a hely felszabadítása előtt annak tartalmát elmentjük egy segédváltozóba:
for (p = fej; p != NULL; p = q) { q = p->kovetkezo; free(p); }
A8.7. pontbanbemutatjuk egymalloc-hoz hasonló tárhelykiosztó függvény megvalósítását. Az általa kiosztott tárterületek tetszőleges sorrendben szabadíthatók fel.
7.8.6. A matematikai függvények
A<math.h>standard headerben húsznál több matematikai függvény van deklarálva, itt most csak a leggyakrabban használatosakat mutatjuk be. Mindegyik függvénynek egy vagy kétdoubletípusú argumentuma van és viszatérési értékedoubletípusú.
| sin(x) | a radiánban adottxérték szinusza; |
| cos(x) | a radiánban adottxérték koszinusza; |
| atan2(y, x) | azy/xárkusz tangense radiánban; |
| exp(x) | azexexponenciális függvény értéke; |
| log(x) | xtermészetes (ealapú) logaritmusa (x>0); |
| log10(x) | x tízes alapú logaritmusa (x>0); |
| pow(x, y) | azxyfüggvény értéke; |
| sqrt(x) | xnégyzetgyöke (x>0); |
| fabs(x) | xabszolút értéke. |
7.8.7. Véletlenszám-generálás
Arandfüggvény egész számok pszeudovéletlen-szerű sorozatát állítja elő. A kapott számok nulla és az<stdlib.h>standard headerben definiáltRAND_MAXérték közé esnek. Ennek felhasználásával a0 <= x < 1tartományba eső lebegőpontos véletlenszámok a
#define frand() ((double) rand() / (RAND_MAX+1.0))
definícióval állíthatók elő. (Ha az adott gépen futó C rendszer könyvtárában már létezik a lebegőpontos véletlenszám-generáló függvény, akkor az valószínűleg kedvezőbb statisztikai tulajdonságokkal rendelkezik, mint az így definiált véletlen szám.)Arandfüggvény kiindulási értéke asrand(unsigned)függvénnyel állítható be. Arandéssrandfüggvények szabványban javasolt hordozható változatát a2.7. pontbanismertettük.
7.9.gyakorlat. Azisupper-hez hasonló függvények helytakarékos vagy időtakarékos változatban írhatók meg. Vizsgálja meg, hogyan lehetséges mindkét változat kialakítása!
| 6. FEJEZET | Tartalom | 8. FEJEZET |

