Programozás I. Széchenyi István Egyetem, Gy r 2014. november 16.
Áttekintés kel kapcsolatos fogalmak deklaráció Több, kompatibilis változat is elképzelhet. Meg kell el znie a fv. hívását. Mindenképp rögzíti a fv. nevét, esetleg visszatérési érték típusát, tárolási osztályát, egyéb attribútumait. prototípus Mint deklaráció, de rendelkezik a formális paraméterek típusáról, sorrendjér l, számáról is. Használata ajánlott, ld. #include ellen rizhet : fv. neve, paraméterezése, visszatérési értéke. deníció Pontosan egy létezik egy programban minden függvényre. Egyben deklaráció is, ha megel zi a hívást. Mint prototípus, de a fv. testével is rendelkezik. Kompatibilisnek kell lennie a megel z prototípusokkal, deklarációkkal. Forrásfájlokban, vagy el fordított könyvtárakban található.
Áttekintés Fv. hívás: vezérlés+paraméter átadás, hívó hívott fv., majd return-nel vissza. Beszélhetünk: formális paraméterekr l: prototípusban+denícióban. aktuális paraméterekr l: híváskor átadott értékek. Paraméter argumentum. Érték szerint kerülnek átadásra (de nem feltétlenül maguk az objektumok, ld. tömbök). Deklaráció, prototípus, deníció #include<stdio.h> osszeg(); int osszeg(); int osszeg(int, int); int osszeg(int a, int b); int osszeg(int c, int d); int main(void) { printf("összeg: %d\n", osszeg (1, 2)); } int osszeg(int i, int j) { return i+j; }
Függvénydeníció Függvény teste tartalmazhatja: lokális változók deklarációit küls leg deklarált tételekre történ hivatkozásokat tevékenységet meghatározó utasításokat de nem tartalmazhatja újabb függvény denícióját! Régi és új stílusú deníció #include<stdio.h> /* Régi (K&R) stílus, kerüljük! int osszeg(i, j) int i, j; { return i+j; } */ /* Modern stílus */ int osszeg(int i, int j) { return i+j; } int main(void) { printf("összeg: %d\n", osszeg (1, 2)); }
Tárolási osztály Függvény deklarációkban használható tárolási osztály specikátorok: semmi fájl hatáskörben elhelyezve küls kapcsolódás extern kapcsolódás: ua., mint a látható, fájl hatáskörben lév deklarációnak, ha létezik ilyen. Egyébként küls kapcsolódás. Blokk hatáskörben (pl. fv. testében) csak ilyen deklaráció szerepelhet! static bels kapcsolódás, csak a tartalmazó forrásban látható. Denícióban is ki kell írni a kulcsszót! Láthatóság: deklaráció/deníció pontjától a forrásfájl végéig
Visszatérési érték Visszatérési érték típusa függvény típusa (paraméterezés is beleértend ) Fontos tudni a vt. érték típusát ( méret) Vt. érték típusa nem lehet függvény és tömb Alapértelmezett típus: int Érték meghatározása: return (esetleg típuskonverzió) void vt. érték: return utáni kifejezést nem értékeli ki + gyelmeztetés
Formális paraméterlista Az egyetlen megengedett tárolási osztály specikátor a register, még az auto használata is tilos! Típusmódosítók használhatóak: const tiltja a balértékként történ felhasználást volatile Ha nincs paraméter void (vö. üres kerek zárójelpár!) Ha van legalább egy formális paraméter, a lista...-ra is végz dhet változó számú paraméterlista (ld. printf(), stdarg.h) aktuális paraméterek hozzárendelési konverzió formális paraméterek
Formális paraméterlista Mi lesz konstans? #include<stdio.h> void hiba(const int n, const char s[]) { /* n++; error: increment of read-only parameter `n' */ /* s[0] = 'X'; error: assignment of read-only location `*s' */ s++; /* OK */ printf("%d: %s\n", n, s); } int main(void) { hiba(123, "I/O hiba"); } Kimenet 123: /O hiba
Formális paraméterlista Dátumellen rzés, 1. rész #include <stdio.h> #include <stdlib.h> /* Az atoi miatt! */ #define INP 11 /* Az input puffer mérete. */ #include <ctype.h> /* Az isdigit miatt! */ int datume(const char[]); int getline(char[], int); void main(void) { char s[inp+1]; /* Az input puffer. */ printf("dátumellen rzés.\n" "Befejezés üres sorral!\n"); while(printf("\ndátum (ÉÉÉÉ.HH.NN)? "), getline(s,inp)>0) if(datume(s)) printf("érvényes!\n"); else printf("érvénytelen!\n"); }
Formális paraméterlista Dátumellen rzés, 2. rész int datume(const char s[]) { static int honap[] = {0,31,28,31,30,31,30,31,31,30,31,30,31}; int i, ho; if(!s[10] &&!isdigit(s[4]) && s[4]==s[7]) { for(i=0; i<10; ++i) { if(i==4 i==7) ++i; if(!isdigit(s[i])) return 0; } if((i=atoi(s)) >= 1) { honap[2] = 28+(!(i%4)&&i%100!(i%400)); if((ho=10*(s[5]-'0')+s[6]-'0')>=1 && ho<=12 && (i=10*(s[8]-'0')+s[9]-'0')>=1 && i<=honap[ho]) return 1; } } return 0; } int getline(char s[],int lim) {... }
A függvény teste Elhagyható deklarációkból és utasításokból áll Itt deklarált változók alapértelmezés szerint auto tárolási osztályúak, lokálisak Fv. hívás után a vezérlés az els utasításra kerül Visszatérés a fv. végén vagy return utasítással Vt. érték meghatározatlan, ha nem return-nel, vagy kifejezés nélküli return-nel ér véget a fv. futtatása hozzárendelési konverzió
Függvény prototípusa Olyan, mint a fv. deníció, de a fv. teste helyén ; áll Ajánlott használni, mert ellen rizhet a paraméterek száma, típusa, sorrendje konverziók is végezhet ek attribútumok is ellen rizhet ek (pl. static szerepel a denícióban is?) Prototípusbeli paraméterek azonosítóinak tulajdonságai hatáskörük a prototípus (részben vagy teljesen) elhagyhatóak, de dokumentációs célra hasznosak lehetnek tömbök esetén az elemszámot sem kötelez szerepeltetni nem kell egyezniük a denícióban szerepl kkel Változó elemszámú paraméterlista:...
hívása, paraméterek konverziója Ha a hívott függvényt korábban nem deklarálták, akkor a fordító feltételezése: extern int azonosító(); () nincs információ (ellen rzések kikapcsolva) (void) nem fogad paramétert Változó paraméterlista: a... elemeit nem ellen rzi Fv. hívás kifejezés típusa és értéke: fv. vt. érték típusa és értéke Aktuális paraméter kifejezések kiértékelési sorrendje fordítónként változhat (mellékhatások!) Paraméter kifejezések összes mellékhatása megvalósul, mire a hívott fv.-be kerül a vezérlés Függvény és tömb nem adható át paraméterként (de címük igen)
hívása, paraméterek konverziója Érték szerinti átadás formális paraméter módosítása nem befolyásolja a hívóban lév értékeket Ha ismert a prototípus a hívás el tt, akkor szükség esetén hozzárendelési konverziót végez a paramétereken vagy hibaüzenetet ad Paraméterek alapértelmezett konverziója: float double char, short int unsigned char, unsigned short unsigned int
Nem szabványos módosítók, hívási konvenciók Hívási konvenciók: paraméterek, visszatérési érték átadásának módja cdecl (C declaration) paraméter átadási sorrend: jobbról balra, majd visszatérési cím. Verem helyreállítás: hívó függvényben. Visszatérési érték: regiszterekben, esetleg rejtett paraméter és lefoglalt memóriaterület felhasználásával. Különféle bájt-határra igazítások. pascal Átadási sorrend: balról jobbra. Verem helyreállítás: hívott függvényben. (Pl. MS Windows 3.x) stdcall Átadási sorrend: jobbról balra. Verem helyreállítás: hívott függvényben. Visszatérési érték: regiszterekben. (Pl. Win32 API hívások) fastcall Nem szabványosított, paraméter átadás regisztereken keresztül, ha lehetséges (különben vermen át)
Rekurzív függvényhívás Minden fv. hívhatja magát közvetlenül vagy közvetve Minden hívásnál új területet allokálnak a formális paramétereknek és a lokális változóknak Globális és static min sítés változók minden fv. példányban ugyanarra a memóriaterületre hivatkoznak! Rekurzív hatványozás. Ötlet: 3 5 = 3 2 2 3 1 = 243 #include<stdio.h> #define A -3 #define K 5 long hatvany(int alap, unsigned kitevo) { long h; if(!kitevo) return 1; if(kitevo == 1) return alap; h = hatvany(alap, kitevo/2); h *= h; if(kitevo&1) h *= alap; return h; } int main(void) { printf("%d^%d=%ld\n", A, K, hatvany(a, K)); return 0; }
Rekurzív függvényhívás pelda22.c #include <stdio.h> #include <limits.h> void printd(int); int main(void) { int n=-32768; printf("rekurzív példa:\n"); printd(n); } void printd(int n) { int i, j=0; if(n==int_min) { ++n; ++j; } if(n<0){ putchar('-'); n = -n;} if(i=n/10) printd(i); putchar(n%10 + '0' + j); }