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
/* Közelítő integrálás a trapéz szabály segítségével.
 * Az integrálandó függvényt paraméterként kapjuk.
 * 1998. Április 14.  Dévényi Károly, devenyi@inf.u-szeged.hu
 * 2006. Augusztus 14. Gergely Tamás, gertom@inf.u-szeged.hu
 */

#include <stdio.h>
#include <math.h>
                                       /* Az integrálandó függvény típusa */
typedef double (*fuggveny_t)(double x);

static double trapez(fuggveny_t f,               /* f(x)-t integráljuk az */
                     double a, double b,             /* a,b intervallumon */
                     int n) {             /* n részre osztva az interv.-t */
                    /* Közelítő integrálás a trapéz szabály segítségével. */
    const double h = (b - a) / n;
    double area = 0.0;
    int i;                                             /* a ciklusváltozó */

    for (i = 1; i < n; ++i) {                  /* fgv. értékek összegzése */
        area += (*f)(a + i * h);                          /* f(a + i * h) */
    }
    return (area * h + ((*f)(a) + (*f)(b)) / 2.0 * h);   /* (f(a) + f(b)) */
}

static double expx(double x) {
                                         /* az első integrálandó függvény */
    return (exp(x) / x);
}

static double sin2x(double x) {
                                      /* az második integrálandó függvény */
    return sin(2.0 * x);
}

int main() { 
    double a, b;
    int n;

    printf("Az exp(x)/x függvény közelítő integrálja.\n");
    printf("Kérem az integrálási intervallumot ");
    printf("és az osztáspontok számát (a,b,n)!\n");
    printf("a:? "); scanf("%lg%*[^\n]", &a); getchar();
    printf("b:? "); scanf("%lg%*[^\n]", &b); getchar();
    printf("n:? "); scanf("%d%*[^\n]", &n); getchar();
    printf("Az integrál közelítő értéke: ");
    printf("%10.5f\n", trapez(expx, a, b, n));

    printf("Az sin(2x) függvény közelítő integrálja.\n");
    printf("Kérem az integrálási intervallumot ");
    printf("és az osztáspontok számát (a,b,n)!\n");
    printf("a:? "); scanf("%lg%*[^\n]", &a); getchar();
    printf("b:? "); scanf("%lg%*[^\n]", &b); getchar();
    printf("n:? "); scanf("%d%*[^\n]", &n); getchar();
    printf("Az integrál közelítő értéke: ");
    printf("%10.5f\n", trapez(sin2x, a, b, n));
    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: 2023-12-01 10:04:01