Kihagyás

Függvény pointer

Függvény pointer

Függvényre mutató pointer

Eddig kétféleképpen közelítettük meg a pointer típusú változót:

  • Első közelítésben a p pointer típusú változó értéke egy meghatározott típusú dinamikus változó.
  • A másik szerint a p pointer típusú változó értéke egy másik változóhoz tartozó memóriacím.

A harmadik megközelítésben a pointer egy részalgoritmusra is mutathat, amelyet aktuális argumentumokkal végrehajtva a megadott típusú eredményhez jutunk. Ez utóbbi esetben is minden, a pointerekre megengedett művelet elvégezhető: szerepelhet értékadásban, elhelyezhető egy tömbben vagy rekordban, átadható egy függvénynek aktuális paraméterként, lehet egy függvény visszatérési értéke, stb.

Ilyen típust egyszerűen úgy deklarálhatunk, hogy a megfelelő függvényfejben a függvény azonosítóját pointerre cseréljük, vagyis a típus azonosítója elé *-ot írunk.

Mivel a * alacsonyabb priorítású, mint a () (függvényhívás művelet, amivel együtt használjuk), ezért a dereferenciát zárójelpárba kell tenni. Legyen adott egy függvény, például:

1
2
3
double sin2x(double x) {
    return sin(2.0 * x);
}

Egy ilyen függvényre mutató pointer típus definíciója:

1
typedef double (*fuggveny_t)(double);

És a használata:

1
2
3
4
fuggveny_t f = sin2x;
double x, y;
y = (*f)(x); /* Így jobban látszik, hogy f nem egy konkrét függvénynév, */
x = f(y);    /* de így is használható. */

Példa: határozott integrál kiszámítása

  • Problémafelvetés:
    • Az \(e^x/x\) és a \(sin(2x)\) függvények határozott integrálját közelítsük egy beolvasott intervallumon a felhasználó által megadott számú részre osztva az intervallumot!
  • Specifikáció:
    • A probléma inputja \(a\), \(b\) valós számok, az intervallum végpontjai, \(n\) a közelítéshez használandó részek száma.
    • Az output egy valós szám, a határozott integrál értéke.
  • Algoritmustervezés:
    • Készítsünk egy függvényt, ami egy tetszőleges függvénynek képes kiszámolni az integrálját!
    • A fő algoritmusban csak az input adatokat olvassuk be, majd meghívjuk az integráló függvényt az aktuális argumentumokkal, végül kiíratjuk az eredményt.
  • Algoritmustervezés szerkezeti ábrával:
    kep

Jó lenne, ha lenne egy univerzális integrál számító függvény, aminek elég lenne megadni azt, hogy melyik függvény integrálját akarjuk meghatározni adott paraméterek mellett. A probléma kulcsmegoldása ennek az integrálszámító függvénynek a megoldása. Éppen ezért, ezt külön problémaként vizsgáljuk.

  • Problémafelvetés:
    • A paraméterként kapott függvény határozott integrálját közelítsük egy adott intervallumon adott számú részre osztva az intervallumot!
  • Specifikáció:
    • A probléma inputja az integrálandó \(f\) függvény, az \(a\), \(b\) valós számok mint az intervallum végpontjai, és \(n\) mint a közelítéshez használandó részek száma.
    • Az output egy valós szám, a függvény \([a,b]\) intervallumon vett határozott integráljának közelítő értéke.
  • Algoritmustervezés:
    • A trapéz módszer szerint történik a közelítés. A képlet egyszerű átalakításával egy számlált menetű ismétléses vezérlést kapunk.
      kep
    • Az integrál értékét úgy kapjuk, hogy az \([a, b]\) intervallumot felosztjuk \(n\) egyenlő részre. Az egyes részeken megrajzoljuk az ábrának megfelelő trapézokat. A trapézok területével közelítjük a függvény görbéje alatti területet, azaz annak integrálját. Minél több részre osztjuk az intervallumot, a trapézok mérete egyre jobban közelíti az integrál értékét.
  • Algoritmustervezés szerkezeti ábrával:
    kep

Az inicializálás során meghatározzuk a trapézok magasságát. Ez egyfelől függ az \(a\) és \(b\) értékek távolságától, illetve attól, hogy hány részre osztjuk az intervallumot. A trapézok területét úgy kapjuk, hogy az alapok átlagát szorozzuk a trapéz magasságával. Jelen esetben minden trapéz magassága \(h=(a+b)/n\). Az alapok nagysága az adott osztáspontokon az adott függvény értéke. A köztes alapok 2-2 trapézhoz kapcsolódnak, a végpontokhoz tartozók csak egyhez. Kiemelve a magassággal való szorzást a trapézok területösszegének meghatározása során, kapjuk a szerkezeti ábrán szereplő összegzést az összterületre.

A trapez függvény paraméterében kapja meg, hogy mely függvényre számoljon integrált, azaz mely függvény értékeit kell az egyes pontokban meghatározni. Az egyetlen, amit tudnunk kell a paraméterben kapott függvényről, hogy az hogyan hívható meg, azaz mi jellemzi a paraméterezését, visszatérési értékét.

Ehhez definiáljuk a fuggveny_t függvény pointer típust. Jelen esetben ez olyan függvényeket fog képviselni, amik egy double paraméterrel rendelkeznek, és visszatérési értékük típusa is double. A példánkban az általunk definiált expx és sin2x függvények inyen típussal rendelkeznek.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
/* Síkidomok területének és kerületének kiszámítása.
 * 2018. Szeptember 19. Gergely Tamás, gertom@inf.u-szeged.hu
 */

#include <math.h>
#include <stdio.h>

#define PUFFERMERET 128

enum sikidom_tipus_t {
    kor, haromszog, teglalap
};

struct kor_tulajdonsagok_t {
    double r;
};

struct haromszog_tulajdonsagok_t {
    double a, b, c;
};

struct teglalap_tulajdonsagok_t {
    double a, b;
};

struct sikidom {
    enum sikidom_tipus_t tipus;
    union {
        struct kor_tulajdonsagok_t kor;
        struct haromszog_tulajdonsagok_t haromszog;
        struct teglalap_tulajdonsagok_t teglalap;
    };
};

double kor_kerulet(struct kor_tulajdonsagok_t k) {
    return 2.0 * M_PI * k.r;
}

double kor_terulet(struct kor_tulajdonsagok_t k) {
    return M_PI * k.r * k.r;
}

double haromszog_kerulet(struct haromszog_tulajdonsagok_t h) {
    return h.a + h.b + h.c;
}

double haromszog_terulet(struct haromszog_tulajdonsagok_t h) {
    double s = (h.a + h.b + h.c) / 2.0;
    return sqrt(s * (s-h.a) * (s-h.b) * (s-h.c));
}

double teglalap_kerulet(struct teglalap_tulajdonsagok_t t) {
    return 2.0 * (t.a + t.b);
}

double teglalap_terulet(struct teglalap_tulajdonsagok_t t) {
    return t.a * t.b;
}

double kerulet(struct sikidom s) {
    switch(s.tipus) {
    case kor:       return kor_kerulet(s.kor);
    case haromszog: return haromszog_kerulet(s.haromszog);
    case teglalap:  return teglalap_kerulet(s.teglalap);
    default:        return NAN;
    }
}

double terulet(struct sikidom s) {
    switch(s.tipus) {
    case kor:       return kor_terulet(s.kor);
    case haromszog: return haromszog_terulet(s.haromszog);
    case teglalap:  return teglalap_terulet(s.teglalap);
    default:        return NAN;
    }
}

char puffer[PUFFERMERET];

int main() {
    double a,b,c;
    struct sikidom s;
    while(fgets(puffer, PUFFERMERET, stdin)) {
        if(sscanf(puffer, "kor(%lf)", &a) == 1) {
            s.tipus = kor;
            s.kor.r = a;
        } else if(sscanf(puffer, "teglalap(%lf,%lf)", &a, &b) == 2) {
            s.tipus = teglalap;
            s.teglalap.a = a;
            s.teglalap.b = b;
        } else if(sscanf(puffer, "haromszog(%lf,%lf,%lf)", &a, &b, &c) == 3) {
            s.tipus = haromszog;
            s.haromszog.a = a;
            s.haromszog.b = b;
            s.haromszog.c = c;
        } else {
            printf("Ismeretlen formatumu sor!\n");
            continue;
        }
        printf("T= %lf\nK= %lf\n", terulet(s), kerulet(s));
    }
    return 0;
}

A példa program main függvénye bemutatja, hogyan lehet különböző függvény pointerekkel meghívni a trapez függvényt. Látszik, hogy a függvények nevei, mint egy-egy mutató kerülnek átadásra a trapez függvény meghívásai során.


Utolsó frissítés: 2022-11-25 15:25:14