Kihagyás

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:

visszatérési_érték_típusa függvény_neve (argumentumlista)
{
    függvénytörzs
}

--> 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
  • Ú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 int lesz a visszatérési érték típusa
    • Nem ajánlott! Adjuk típust a függvénynek!
  • A void függvényeket kivéve minden függvényben kell min. egy return utasí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 return utasí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(); }
  • 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)