Kihagyás

Egy kis ráadás...

Egy kis ráadás...

Miután megismertük a különböző vezérlési szerkezeteket, az előző fejezetekben azt láthattuk, milyen adattípusokkal rendelkezik a C nyelv, és hogyan tudunk mi magunk is saját típusokat létrehozni.

Sokszor azonban egyéb dolgok is kellhetnek, amik segítik egy program futását, amikkel azok működése, működtetése kényelmesebbé tehető, illetve amiknek ismerete elengethetetlen akkor, amikor programozunk egy adott nyelven. Ezeket próbáljuk összeszedni ebben a fejezetben.

Műveletek precedenciája

Számos műveletet megismertünk az eddigiek során. Bár a legtöbbször el is hangzott, azért nem árt, ha összegzésképp megnézzük, hogy ezek a műveletek hogyan viszonyulnak egymáshoz.

A műveletek precedenciája, illetve feldolgozási iránya (asszociativitása) megadja, hogy egy kifejezésen belül a műveletek kiértékelésének mi lesz a helyes sorrendje. Nyilván ezeknek az ismerete sokszor nélkülözhetetlen annak érdekében, hogy a kódunk valóban azt a tevékenységet végezze, ami a szándékunk volt.

A tanult műveleteket, kiértékelési irányukat és precedenciájukat az alábbi táblázatban foglaljuk össze:

Műveletek Asszociativitás C operátor
egyoperandusú postfix
elemkiválasztás
mezőkiválasztás
függvényhívás
\(\rightarrow\) x++, x--
[]
., ->
f()
egyoperandusú prefix
méret
típuskényszerítés
\(\leftarrow\) +, -, ++x,--x, !,~, &,*
sizeof
(type)
multiplikatív \(\rightarrow\) *,/, %
additív \(\rightarrow\) +,-
bitléptetés \(\rightarrow\) <<,>>
kisebb-nagyobb relációs \(\rightarrow\) <=, >=, <,>
egyenlő-nem egyenlő relációs \(\rightarrow\) ==, !=
bitenkénti 'és' \(\rightarrow\) &
bitenkénti 'kizáró vagy' \(\rightarrow\) ^
bitenkénti 'vagy' \(\rightarrow\) |
logikai 'és' \(\rightarrow\) &&
logikai 'vagy' \(\rightarrow\) ||
feltételes \(\leftarrow\) ?:
értékadó \(\leftarrow\) =, +=, -=, *=, /=, %=, >>=, <<=, &=,&=,^=,|=,
szekvencia \(\rightarrow\) ,

Parancssori argumentumok

A C nyelvet támogató környezetekben lehetőség van arra, hogy a végrehajtás megkezdésekor a programnak parancssori argumentumokat vagy paramétereket adjunk át. Ez sokszor megkönnyítheti a program kezelését, főleg akkor, ha a parancssorban minden olyan információt át tudunk adni, amit korábban csak a felhasználó által való interakció által tudom meg a program.
Amikor az operációs rendszer elindítja a programot, azaz meghívja a main függvényt, a hívásban két argumentum szerepelhet:

  • Az első (általában argc névre hallgat) azoknak a parancssori argumentumoknak a darabszáma, amelyekkel a programot meghívtuk.
  • A második argumentum (általában argv) egy mutató egy sztring tömbre, amely a parancssori argumentumokat tartalmazza. Egy karakterlánc egy argumentumnak felel meg.
    • Megállapodás szerint C-ben az argv[0] az a név, amellyel a programot hívták, így azargc értéke legalább 1.
    • Számíthatunk arra is, hogy argv[argc]==NULL
  • Mivel az argv egy mutatótömböt megcímző mutató, a main függvényt többféleképpen deklarálhatjuk.
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#include <stdio.h>
int main(int argc, char **argv) {
/* vagy így is lehet az argv-t deklarálni
int main(int argc, char *argv[]) {
*/
    int i;
    printf("argc = %d\n\n", argc);
    for (i = 0; i < argc; ++i) {
        printf("argv[%d]: %s\n", i, argv[i]);
    }
    return 0;
}

Elmentve a fenti programot arg.c néven, fordítsuk le és futtassuk a következő módon:

Kimenet

$ gcc -o arg arg.c
$ ./arg alma korte szilva barack palinka
argc = 6
argv[0]: ./arg
argv[1]: alma
argv[2]: korte
argv[3]: szilva
argv[4]: barack
argv[5]: palinka

Példa: Függvény határozott integráljának kiszámítása parancssori argumentumokkal
  • Problémafelvetés:
    • Adott függvény 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 az integrálandó függvény neve, \(a\), \(b\) valós számok, az intervallum végpontjai, \(n\), a közelítéshez használandó részek száma.
    • Az inputot a parancssorból vegye a program.
    • Az output három valós szám: a megadott függvény értéke \(a\) és \(b\) pontokban, valamint a határozott integrál értéke.
    • Ha az argumentumok száma nem megfelelő, akkor hibaüzenetet írunk ki.
    • A függvény neve lehet az, hogy help, és ekkor csak tájékoztató szöveg jelenik meg a választható függvények nevéről.
  • Algoritmustervezés:
    • A probléma inputja az integrálandó függvény neve, \(a\), \(b\) valós számok, az intervallum végpontjai, \(n\), a közelítéshez használandó részek száma.
    • Az inputot a parancssorból vegye a program.
    • Az output három valós szám: a megadott függvény értéke \(a\) és \(b\) pontokban, valamint a határozott integrál értéke.
    • Ha az argumentumok száma nem megfelelő, akkor hibaüzenetet írunk ki.
    • A függvény neve lehet az, hogy help, és ekkor csak tájékoztató szöveg jelenik meg a választható függvények nevéről.
  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
104
105
106
107
108
109
110
111
112
113
114
/* Közelítő integrálás a trapéz szabály segítségével.
 * Az integrálandó függvény nevét a parancssorból kapjuk.
 * Meg kell adnunk az intervallumot is és a finomságot is.
 * Kérhető help is.
 * 1998. Április 14.  Dévényi Károly, devenyi@inf.u-szeged.hu
 * 2020. Július 24. Gergely Tamás, gertom@inf.u-szeged.hu
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <errno.h>

typedef struct tablaelem_t {
    char *nev;                             /* A függvény neve szövegesen  */
    double (*fuggveny)();                  /* A függvényre mutató pointer */
} tablaelem_t;

static double help();

static double expx();

static double sin2x();

tablaelem_t tablazat[] = {
    { "sin",   sin   },
    { "tan",   tan   },
    { "cos",   cos   },
    { "expx",  expx  },
    { "sin2x", sin2x },
    { "help",  help  },
    {  NULL,   NULL  }                          /* A táblázat végét jelzi */
};

static double expx(double x) {
    return (exp(x) / x);
}

static double sin2x(double x) {
    return sin(2.0 * x);
}

double help(tablaelem_t *tp) { 
    printf("Kérem válasszon a következő függvények közül: ");
    for (; tp -> nev; ++tp) {
        printf("%s ", tp -> nev);
    }
    printf("\nMeg kell adnia az intervallumot és a finomságot.\n");
    exit(EXIT_SUCCESS);
}
                                       /* 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 intervallumot */
                    /* 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);
    }
    return (area * h + ((*f)(a) + (*f)(b)) / 2.0 * h);
}

int main(int argc, char **argv) {
    tablaelem_t *tp;
    double a, b;
    int n;
    char *kiiratas_formatuma = "\n %s(%s)= %g\n %s(%s)= %g\n %s integrálja= %g\n";

    if (argc > 5) {
        errno = E2BIG;
        perror("A hiba az, hogy");
        exit(EXIT_FAILURE);
    }

    for (tp = tablazat;                      /* a függvény nevét keressük */
          (tp -> nev && argv[1] && strcmp(tp -> nev, argv[1]));
          tp++
        );                                              /* üres ciklusmag */

    if (tp -> nev == NULL) {                          /* nem találtuk meg */
        printf("%s függvény még nincs megvalósítva\n", argv[1]);
        exit(EXIT_SUCCESS);
    }

    if (tp -> fuggveny == help) {
        help(tablazat);
    }

    if (argc < 5) {
        perror("A hiba az, hogy kevés az argumentum");
        exit(EXIT_FAILURE);
    }

    a = atof(argv[2]);                 /* Konvertál sztring-ről double-re */
    b = atof(argv[3]);
    n = atoi(argv[4]);                     /* Konvertál string-ről int-re */
    printf(kiiratas_formatuma,            /* Pointer a formátum sztringre */
           argv[1],                          /* Pointer a függvény nevére */
           argv[2],                             /* Pointer az első számra */
           (*tp -> fuggveny)(a),               /* A függvény értéke a-nál */
           argv[1],                          /* Pointer a függvény nevére */
           argv[3],                           /* Pointer a második számra */
           (*tp -> fuggveny)(b),               /* A függvény értéke b-nél */
           argv[1],                          /* Pointer a függvény nevére */
           trapez(*tp -> fuggveny, a, b, n)      /* A függvény integrálja */
        );
    exit(EXIT_SUCCESS);
}

A program elején definiálunk egy tablaelem_t típust. Ez egy olyan struktúrát fog definiálni, ahol egy adott sztringet rendelünk majd egy adott függvényhez. Ez a táblázat köti össze a felhasználó által megjelölt függvényeket azok tényleges megvalósításával.

Figyeljük meg, hogy a tablaelem_t típusban a függvény pointer paraméter listája üres. A későbbiekben látjuk, hogy ez azért kell, mert így a későbbiekben tetszőleges paraméterlistával ellátott függvény címe átadható ezen keresztül.

Ahhoz, hogy ez a táblázat könnyen bővíthető legyen, a méretét külön nem adjuk meg, ehelyett úgy definiáljuk, hogy utolsó értékében az aktuális táblaelem úgy van definiálva, hogy annak mindkét mezője NULL értékkel rendelkezik.

A program használata során a felhasználó megadja a program parancssorában annak a függvénynek a nevét, aminek az integrálját akarja meghatározni. Illetve megadja a szükséges egyéb paramétereket. Függvények közül csak olyan függvényt adhat meg, amelyet a tablazat definiál. Ezek olyan matematikai függvények (double paraméterrel és visszatérési értékkel), amelyeket definál a math.h, de akár mi magunk is definiálhatunk.

Illetve van egy bónusz függvény, a help, ami szintén eleme a táblázatnak. Ha a felhasználó ezt választja, nem számítódik integrál, akkor csak egy tájékoztató üzenettel tér vissza a program.

Nézzük is meg egy kicsit részletesebben ezt a help függvényt. A feladata annyi, hogy a tablazat elemeit végigjárja, és kiírja azon függvények nevét, amelyeket a felhasználó a parancssorban megadhat. Ehhez paraméterében ő az első táblaelemre mutató pointert kapja meg. Egy egyszerű ciklussal bejárva a táblázatot pedig információt ad a hívható függvényekről.
A függvény utolsó utasítása az exit függvény meghívása, amellyel a program végrehajtása befejeződik. A paraméterében az EXIT_SUCCCESS flag által azt az üzenetet kapja az operációs rendszer, hogy a program normálisan fejeződött be. Láthatjuk később a main függvényben, hogy amikor nem megfelelő paraméterekkel, illetve paraméter számmal hívja a felhasználó a programot, a hibaüzenet az EXIT_FAILURE. Bizonyos hibákra adott hibakód létezik. Érdemes ezeket megfelelően beállítani, egyes esetekben ezek az értékek segíthetik a program helyes használatát.


Utolsó frissítés: 2020-10-16 10:10:29