5. gyakorlat
Téma: függvények
Felhasználási előnyök, területek
- modularizálás
- kódismétlések elkerülése
Függvények részei
Egy függvény általános szintaxa:
--> Függvény deklarációja
- Függvény deklarációja: Amikor bejelentjük egy függvény létezését
- Ezt akárhányszor meg lehet tenni
- A függvény neve határozza meg magát a függvényt!
- Két függvénynek nem lehet ugyanaz a neve, de különböző a visszatérési értéke típusa és az argumentumlistája
- A visszatérési érték típusa és az argumentumlista nem határozza meg a függvényt
- A C nyelv nem támogatja a típusfüggetlen megoldásokat (generikus programozás)
Példa:
#include <stdio.h>
// ezt lehet
int fuggveny(int a);
int fuggveny(int a);
// ez hibás
float fuggveny(char a);
int main()
{
return 0;
}
--> Függvény definíciója
- Függvény definíciója: Amikor megadjuk a függvényben, hogy milyen utasításokat hajtson végre
- A függvény deklarációja elhagyható, ha van definíció
- A függvény deklarációja és definíciója meg kell, hogy egyezzen
Példa:
#include <stdio.h>
// külön deklarált és definiált függvény
int fuggveny(int a);
int fuggveny(int a)
{
return a + 1;
}
// egyszerre deklarált és definiált függvény
int szumma(int a, int b)
{
return a + b;
}
int main()
{
return 0;
}
--> Függvény hívása
- Függvény hívása: Amikor használni szeretnénk a függvényt
- Nem kötelező eltárolni a visszatérési értéket
Példa:
#include <stdio.h>
int szumma(int a, int b)
{
return a + b;
}
int main()
{
// függvény hivás általános szintaxa: függvény_neve (argumentumok);
// eltároltuk a visszatérési értéket
int a = szumma(2, 3);
printf("szum = %d\n", a);
// nem tároltuk el a visszatérési értéket
printf("szum = %d\n", szumma(2, 3));
return 0;
}
--> Visszatérési érték típusa
- A függvény típusa a visszatérési érték típusa (pl. int)
- Tetszőleges típus adható meg
- Ha
void-ot adunk meg, akkor nincs visszatérési érték
- Ha
- Úgy is deklarálhatunk, definiálhatunk egy függvényt, hogy nem adjuk meg a visszatérési érték típusát
- Ilyenkor automatikusan
intlesz a visszatérési érték típusa - Nem ajánlott! Adjuk típust a függvénynek!
- Ilyenkor automatikusan
- A
voidfüggvényeket kivéve minden függvényben kell min. egyreturnutasítás- Ezzel adjuk meg, hogy mit adjon vissza a függvény
- Több is lehet belőle (pl. if-else utasításokban)
- A
returnutasítás megszakítja a függvény futását
Példa:
#include <stdio.h>
// float típusú
float avg(int a, int b)
{
return ((float)a + (float)b) / 2;
}
// int típusú
int szumma(int a, int b)
{
return a + b;
}
// nincs visszatérési értéke (csak végrehajtódik, ami benne van)
void feladat()
{
for (int i = 0; i < 10; i++)
{
printf("%d\n", i);
}
}
int main()
{
printf("avg = %f\n", avg(3, 10));
printf("szum = %d\n", szumma(2, 3));
feladat();
return 0;
}
--> Argumentumlista
- Amiket átadunk a függvénynek
- Tetszőleges típusokat használhatunk
- Egy függvénynek tetszőleges mennyiségű argumentuma lehet
- Vesszővel kell elválasztani
- Lehet üres az argumentumlista
- Ha ilyenkor mégis adunk neki argumentumokat híváskor, akkor azok nem lesznek elérhetőek a függvényben
- Az átadott argumentumok nem a hívó fél változói, hanem lokális változók lesznek, új memóriaterületen tárolva
- Ha a függvényben változtatást hajtunk végre rajtuk, az nincs hatással a hívó részen lévő változókra
- Az előző alól kivétel, ha az argumentumlistában tömb szerepel
- Ilyen esetben a függvényben módosított értékek a hívó fél oldalán is megtörténnek
- Oka: a tömb nem másolódik át, csak a memóriacímét tartalmazó pointer
- Fontos: a tömb méretét is át kell adnunk (elhagyható, de akkor nem tudnunk rá hivatkozni)
Példa:
#include <stdio.h>
char harmadikElem(int meret, char tomb[meret])
{
if (meret < 3)
{
return '*';
}
else
{
return tomb[2];
}
}
int main()
{
// általánosan: return_type function(int size, array_type array[size]);
char alma[5] = {'a', 'b', 'c', 'd', 'e'};
printf("A tömb 3. eleme: %c\n", harmadikElem(5, alma));
return 0;
}
Rekurzív függvények
Nagyon általános szintax:
visszatérési_érték_típusa függvény_neve (argumentumlista)
{
utasítások1;
függvény önhívása;
utasítások2;
}
- Olyan függvény, ami önmagát hívja
- Az 1., a 2. és akár mindkét utasításcsoport elhagyható
- A legegyszerűbb rekurzív függvény:
void foo() { foo(); }
- A legegyszerűbb rekurzív függvény:
- Fontos: A rekurzív függvény úgy működik, hogy a memóriába új példányt helyez, memóriafoglalással, új lokális változókkal, új utasításokkal
- Ugyanannyi memóriát foglal mindig, mint az eredeti (gondoljunk Micimackó és a rekurzió esetére...)
- Véges memóriánk van, ezért segmentation fault-ba futhatunk
- Nem hatékony a rekurzió, mert mindig egyre nagyobb távolságokat kell megtenni a memóriában
- Ajánlott inkább ciklusokat használni rekurzió helyett
Végtelen ciklus rekurzióval
- Ha nincs megszakítási feltétel megadva (olyan eset, amikor már nem hívjuk meg a függvényt ismételten), akkor végtelen ciklus jön létre
- Rekurzív függvény implementálásakor a legfontosabb feladatunk a kilépési feltétel megadása
Tail recursion (farokrekurzió)
- Olyan rekurzív függvény, melynek a return utasításában van az önmagát meghívó kód
- Emiatt ez könnyen átalakítható ciklussá (a fordítók az optimalizáció során meg is teszik)