Függvények Rögtön az elején egy programozás-technikai kérdés: Van-e eljárás? Nincs? Vagy mégis? A megoldást a kimeneti típus nélküli függvények adják. Ezeknél a return ..; utasítás minden további következmény nélkül egyhagyhatü. Pl.: prototípusa: void kiir(int szam1, int szam2); Függvény általános alakja (új): függvénynév() { } visszatérés lehet bármilyen skalár, vagy struktúra (esetleg union) (minden kiértékelési ágban el 'kell' helyezni, de nem muszáj) függvény paraméterei - új és régi forma Függvény deklarációja deklaráció csak függvény név és visszatérési érték prototípus teljes(paraméterdekl. lista is) int proba(int, int); == int proba(int x, int y); de int proba2(void); != int proba2(); Többször is megjelenhet a kódban, azonban ezeknek meg kell egyezniük. Függvények visszatérési típusa Típus bármely skalár (standard típusok + felsorolási és mutató típus), struktúra. Ha expliciten nem határozzuk a visszatérési típust, akkor automatikusan az int típusú lesz a függvényérték. Visszatérési érték nélküli függvényt void típus felhasználásával készíthetünk, ezáltal eljárás jellegű függvényt hozunk létre. Függvényattribútumok használata A visszatérési típus után adhatjuk meg az attribútumokat, amelyek a függvény alapértelmezett működését módosítják. Ezek a következők: attribútum leírás pascal Pascal függvényhívási konvenciók használata cdecl C függvényhívási konvenciók használata interrupt megszakítás-kezelő függvény near közeli függvényhívás assembly függvényekhe z far távoli függvényhívás huge távoli függvényhívás, az adatszegmens átadásával Függvény hívás kif1(); például: int fgv(int x, double y); // prototípusú függvény esetén: … int eredmeny; eredmeny = fgv(1,2.5); Változók átadása függvénynek · érték szerinti átadás o másolatot kap a függvény o nem hat vissza az eredetire o pl.: … int szamol(int x, int y); … void main() { int eredmeny, valtozo_1 = 10, valtozo_2 = 30; eredmeny = szamol(valtozo_1, valtozo_2); } int szamol(int x, int y) { x += y; return x; } Mit is látunk itt: az átadott x értéket módosítjuk az argumentumlista másik elemének értékével, majd a visszatérési értékben visszaadjuk ezt a módosított értéket értékeljük a változók tartalmát: - az eredmeny a két érték összege lesz - a valtozo_1 értéke nem változik ugyanis a függvény meghívásakor a változó értéke átmásolódik a függvény memóriaterületére, és ott módosul az utasítások nyomán; a függvényt elhagyva a változó (memóriaterület) megszünik, így a módosítás nem hat vissza. · cím szerinti átadás o címet ad át o indirekt hivatkozást ad át o a memória terület tartalma megváltozhat § pl.: pointer o példa: void szamol(int *, int *); void main() { int i, j; … szamol(&i,&j); … } void szamol(int* x, int* y) { *x += 3; *y -= 5; } Függvények tömbje int (*fgvek[4]) (int,int) = {fg1,fg2}; int fg1(int,int); eredm = (*fgvek[1])(x,y); Struktúrák, tömbök átadása függvénynek Struktúra átadása Argumentumként akár érték szerinti, akár mutató segítségével átadhatjuk függvényeinknek a saját magunk által létrehozott változókat. A lehetőségek közül a pillanatnyi helyzetet értékelve kell dönteni; a memóriaigény, teljesítmény szempontjából a pointer átadása a hatékonyabb. Emellett a választást a változók blokk elhagyása utáni érvényessége is befolyásolja (a mutató címet hordoz, így a memóriaterületen történi változás; lásd feljebb). A struktúra létrehozása nem befolyásolja a használatot, vagyis típust létrehozva, és struktúrát definiálva is használhatjuk őket: pl.: struct ember { char* nev; int eletkor; }; 1. typedef struct ember emberek; // gyakorlatilag szinonímát készítünk a struktúrához int eletkor_kulonbseg(emberek Aladar, emberek Elek) { int kulonbseg; kulonbseg = Aladar.eletkor – Elek.eletkor; return kulonbseg; } 2. int eletkor_kulonbseg(struct ember Aladar, struct ember Elek) { int kulonbseg; kulonbseg = Aladar.eletkor – Elek.eletkor; return kulonbseg; } Tömbök átadása Sajnos meg kell különböztetnünk az egy- és kétdimenziós (esetleg több) eseteket, ezeket külön tárgyaljuk. Vektor Bármely vektor átadásnál nem maga a vektor, hanem az első elemére mutató pointer kerül át a függvényhez (mutató – tömb analógia), ennek megfelelően érték szerinti átadás ebben az esetben nincs, amely magával hozza, hogy bármilyen változtatás a függvényblokk elhagyása után is érvényben marad. Ezt a const típusminősítővel korlátozhatjuk, vagyis a mutatott objektum értéke nem változhat meg. Figyeljünk oda a használatára! Ennek megfelelően az átadott tömb (mutató) elemszámát nem ismerjük, ezt általában külön argumentumként adjuk át. Kivételt képeznek a karaktertömbök, mert ezek tartalmazzák a 0 byte-ot, így a végét könnyen determinálhatjuk. pl.: int p[10]; void fgv(int *pointer, int n) void fgv(int pointer[], int n) // a két feljéc ekvivalens egymással, mindkettő csupán a típusát hatáozza meg. { int i, for(i=0;i vagy #include „filenév” Makrók használata #define – foglalás #undef – feloldás A használandó azonosítók – konvenció szerint – csupa nagybetűvel írandóak. Az előfeldolgozó a forrás átvizsgálásakor lecseréli a makró nevet a helyettesítendő szöveggel. Objektumszerű makrók készítése Ekkor az előbb látott direktíva egyszerűbb alakját használjuk: #define azonosító helyettesítő szöveg pl.: #define NEV „János” #define TRUE 1 #define FALSE 0 #define BOOL int stb. Függvényszerű makrók készítése Ezek az úgynevezett paraméterezett makrók. Általános definíciója: #define azonosito(param_lista) (helyettesitendo_szoveg) A makró meghívása: azonosito(argumentumlista) Nyilvánvalóan a paraméterlista és argumentumlista elemszámának meg kell egyezni, ezzel együtt is készíthető paraméter nélküli makró. példa: #include #define abs(x) ((x) < 0 ? (-(x)) : x) #define plusz(x,y) ((x)+(y)) #define csereInt(x,y) {int z; z = x; x = y; y = z;} #define csereStr(x,y) {char* z; z = x; x = y; y = z;} void main() { int szam_1 = 10, szam_2 = 20; char* str_1 = „Hello világ!”; char* str_2 = „Hello world”; printf("Két szám összege: %d\n",plusz(szam_1,szam_2)); // Két szám cseréje printf("Számok a csere előtt: %d, %d\n", szam_1, szam_2); csereInt(szam_1, szam_2); printf("Számok csere után: %d, %d\n", szam_1, szam_2); // Két szöveg felcserélése, fordítás printf("Fordítás előtt: %s, %s\n", str_1, str_2); csereStr(str_1,str_2); printf("Fordítás után: %s, %s", str_1, str_2); } Megjegyzések: · a makró törzsében a paramétereket kerek zárójelben kell használni, különben más értelmezést kaphatnak pl.: a plusz(x,y) makró minden esetben helyes eredményt ad, viszont egy szorzatot tartalmazó függvényszerű makró nem. · miután szöveghelyettesítésről van szó, így megadhatunk teljes utasításblokkokat is, mint ez a két csereXxx(x,y) makrónál is látható. · ha valamely átadott értéket sztringként szeretnénk felhasználni ## operátor nagy segítség lehet a számunkra, lássunk egy példát: #include #define meg(a) ((x##a) + 10) void main() { int x1 = 8, x2 = 108; printf("Az első változó: %d\n",meg(1)); printf("A második változó: %d",meg(2)); } eredménye: Az első változó: 18 A második változó: 118; · sortörést is el lehet helyezni: \ jellel. Elődefiniált makrók ANSI szabvány elődefiniált makrója: Makró Leírás __DATE_ Fordítás dátuma (string) _ __TIME_ Fordítás időpontja (string) _ __FILE_ Forrás file neve (string) _ __LINE_ Aktuális sor sorszáma (int) _ __STDC_ Értéke 1, ha a fordító ANSI C, _ különben nem definiált. Ezt a C nyelvek továbbiakkal egészítik ki. Feltételes fordítás #if konstans kifejezés program részlet_1 #else program részlet_2 #endif pl.: #define DEBUG 1 … #if DEBUG … #else … #endif