Függvények Programozás I. Hatwágner F. Miklós Széchenyi István Egyetem, Gy®r
2014. november 16.
Áttekintés Függvényekkel 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ó. Hatwágner F. Miklós
Függvények
Á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; } Hatwágner F. Miklós
Függvények
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)); } Hatwágner F. Miklós
Függvények
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
Hatwágner F. Miklós
Függvények
Visszatérési érték
Visszatérési érték típusa 6= 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
Hatwágner F. Miklós
Függvények
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
Hatwágner F. Miklós
Függvények
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
Hatwágner F. Miklós
Függvények
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
/* 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"); } Hatwágner F. Miklós
Függvények
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) { ... } Hatwágner F. Miklós
Függvények
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ó
Hatwágner F. Miklós
Függvények
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: ...
Hatwágner F. Miklós
Függvények
Függvények 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)
Hatwágner F. Miklós
Függvények
Függvények 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
Hatwágner F. Miklós
Függvények
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) Hatwágner F. Miklós
Függvények
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:
2
−35 = −32 × −31 = −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; } Hatwágner F. Miklós
Függvények
Rekurzív függvényhívás pelda22.c #include <stdio.h> #include 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); }
Hatwágner F. Miklós
Függvények