IT rekvalifikace s garancí práce. Seniorní programátoři vydělávají až 160 000 Kč/měsíc a rekvalifikace je prvním krokem. Zjisti, jak na to!
Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

Lekce 13 - Funkce s variabilním počet a typem argumentů v jazyce C

V minulej lekcii, Testování v jazyce C pokračování, bola reč o TDD.

Nedávno som sa pri potulkách internetom stretol s celkom zaujímavou knižnicou a rozhodol som sa, že vás s ňou trochu zoznámim. Jedná sa o knižnicu stdarg.h.

Všetky štandardné vstupno výstupné funkcie (knižnica stdio.h) s formátovaním používajú knižnicu stdarg.h. Sú to všetky funkcie, ktoré majú v časti názvu slovo printf alebo scanf, napríklad: printf(), sprintf(), fprintf(), a pod., scanf(), sscanf(), fscanf(), a pod... Keď som si prezeral súbor stdio.h, bol som veľmi prekvapený, koľko je tam funkcií, s ktorými som sa ešte nepotýkal.

V tomto článku si v krátkosti vysvetlíme, ako sa funkcia používa v prípade, že chceme použiť premenlivý počet argumentov.

Ďalej si ukážeme príklad funkcie, kde použijeme premenlivý počet a aj premenlivé typy argumentov.

A nakoniec si vysvetlíme ako s pomocou knižnice stdarg.h pracovať vo funkcii s argumentami ako sú pointery na funkcie.

Dajme tomu, že ste dostali za úlohu napísať jednoduchý program s týmto zadaním:

Zadání

„Napíšte funkciu, ktorá zo zadaných celých čísiel (argumentov) typu 'int' vyberie najväčšie a vráti ho. Prvé číslo stanovuje počet čísiel, z ktorých sa bude vyberať najväčšie. Počet zadaných čísiel môže byť rôzny.“

Volanie funkcie môže byť takéto:

1. najvacsie(2, 25, 68);  // 2 – počet argumentov, 25 a 68 – argumenty ( výsledok 68)
2.najvacsie(5, 56, 58, 98, 45, 33);  // 5 – počet argumentov, 56 …33 - argumenty (výsledok 98)

Priznám sa, že som dlho premýšľal, ako to napísať bez použitia stdarg.h a nakoniec som na nič neprišiel. Ale ak má niekto nejaký nápad, napíšte to v komentároch, doplním to sem.

Klasicky by sa to dalo riešiť asi len takto:

1. int najvacsie(const int pocet, int a, int b);
2. int najvacsie(const int pocet, int a, int b, int c, int d, int e);

Ale to nerieši jednu funkciu na variabilný počet vstupov, preto sa pozrieme ako to napísať s použitím knižnice stdarg.h.

Najprv som napísal to čo chcem dosiahnuť, čiže funkciu main().

#include <stdio.h>

int main(void)
{
    printf("Najvacsie cislo: %d\n", najvacsie(4, 5, 78, 78, -46));
    printf("Najvacsie cislo: %d\n", najvacsie(7, 2565, 1178, 925, 12546, 2568, 12547, 1234));
    printf("Najvacsie cislo: %d\n", najvacsie(4, -85, -78, -78, -46));

    return 0;
}

Teraz si napíšeme funkciu s variabilným počtom argumentov.

Úplný funkčný prototyp funkcie vyzerá takto:

int najvacsie(const int pocet_argumentov, ...);

Pekné, že áno?

Samotná funkcia je tu:

int najvacsie(const int pocet_argumentov, ...) // 1
{
    int i = -1, argumenty_int, pom = INT_MIN, vysledok; // 3
    va_list argumenty; // 4
    va_start(argumenty, pocet_argumentov); // 5

    while((++i) ^ pocet_argumentov) { // 7
        argumenty_int = va_arg(argumenty, int); // 8
        vysledok = pom > argumenty_int ? pom : argumenty_int; // 9
        pom = vysledok;
    }
    va_end(argumenty); // 12

    return vysledok;
}

Vysvetlíme si jednotlivé časti kódu:

  • 3 – deklarácia premenných
  • 4 – vytvorenie ukazovateľa, ktorý môže ukazovať na blok argumentov /typ va_list z knižnice/
  • 5 – spustí spoluprácu s blokom argumentov /funkcia va_start() z knižnice/
  • 6 – prechod všetkými argumentami v bloku argumentov
  • 7 – vloženie získaného argumentu do premennej
  • 8-9 – výpočet najväčšieho čísla
  • 12 – ukončenie práce s blokom argumentov /funkcia va_end() z knižnice/

A to je celé.

Ďalej tu mám pre vás ukážku funkcie miniprintf() s variabilným počtom a typom argumentov.

Ukázka funkce miniprintf()

Je to funkcia, ktorá vypíše na obrazovku zadané premenné typu int, char a string, ale dala by sa samozrejme rozšíriť aj na ostatné formátovacie prvky.

#include <stdio.h>
#include <stdarg.h>

void miniprintf(char *fmt, …) // *fmt je pointer na makro, ktoré rieši formátovacie prvky
{
    va_list ap;
    int d;
    char c, *s;

    va_start(ap, fmt);
    while (*fmt)
        switch(*fmt++) {
    case 's':  /* string */
        s = va_arg(ap, char *);
        printf("string %s\n", s);
        break;
    case 'd':  /* int */
        d = va_arg(ap, int);
        printf("int %d\n", d);
        break;
    case 'c':  /* char */
        c = (char) va_arg(ap, int);
        printf("char %c\n", c);
        break;
    }
    va_end(ap);
}

int main(void)
{
    int i = 25;
    char c  = 'A';
    char *s = "test";

    miniprintf("%d\n%c\n%s\n", i, c, s);

    return 0;
}

Musím sa priznať, že ako pracuje *fmt, __fmt alebo *format v týchto funkciách mi nie je celkom jasné, aj keď som sa dosť túlal po nete. Ak to niekto vie, prosím o doplnenie.

Na koniec si vysvetlíme prácu funkcie s argumentami typu pointer na funkciu.

Práca funkcie s argumentami typu pointer na funkciu

Najskôr si pozrieme dve funkcie main() (samozrejme, že kódu je v programe podstatne viac), ktoré robia to isté, ale v kóde sa pomerne dosť odlišujú.

int main(void)
{
    int vyber;

    srand(time(0));
    printf("Ak je to tvoja prva hra, stlac cislo 1, ak nie, stlac cislo 2 ");
    scanf(" %d", &vyber);
    if (vyber == 1) {
        vytvor_pole(pole);
        zahajenie();
        ulohy(pole);
        vypis(pole);
        uloz_txt(pole);
        uvolni_pole(pole);
    }
    else {
        vytvor_pole(pole);
        nacitaj_txt(pole);
        zahajenie();
        vypis(pole);
        ulohy(pole);
        vypis(pole);
        uloz_txt(pole);
        uvolni_pole(pole);
    }

    return 0;
}
int main(void)
{
    int vyber;

    srand(time(0));
    printf("Ak je to tvoja prva hra, stlac cislo 1, ak nie, stlac cislo 2 ");
    scanf(" %d", &vyber);
    if (vyber == 1) {
        foo(7, vytvor_pole, zahajenie, ulohy, vypis, uloz_txt, uvolni_pole);
    }
    else {
        foo(7, vytvor_pole, nacitaj_txt, zahajenie, vypis, ulohy, vypis, uloz_txt, uvolni_pole);
    }

    return 0;
}

Určite ste si všimli, že jediný rozdiel je v tom, že v prvej funkcii sa funkcie volajú klasicky, teda názvom, zátvorkou a vstupnými parametrami a v druhej za pomoci funkcie foo(), ako jej variabilné parametre, ale už sa volajú iba názvom.

Môže to byť dosť výhodné, ak treba pri písaní, alebo ladení programu často s funkciami manipulovať, takto je to myslím rýchlejšie a jednoduchšie.

Ako teda vyzerá funkcia foo()?

Úplný funkčný prototyp je jednoduchý:

void foo(const int, ...);

a vlastne ani samotný kód nie je zložitý:

void foo(const int num_of_func, ...)
{
    va_list args;
    int i;
    va_start(args, num_of_func);
    for (i = 0; i < num_of_func; i++) {
        void (*bar)() = va_arg(args, void (*)());
        (*bar)();
    }
    va_end(args);
}

Oproti tomu, čo sme si tu dnes povedali, sú tu len dve „odlišnosti“:

void (*bar)() = va_arg(args, void (*)());

vloží do prázdneho pointera void (*bar)() pointer na konkrétnu funkciu z bloku

a

(*bar)();

zavolá konkrétnu funkciu.

Ako som už v úvode písal, nemám s tým ešte veľké skúsenosti, ale aj napriek tomu som vás chcel nasmerovať na niečo, čo ste možno ešte nepoužili a bolo by to pre vás výhodné.

Do prílohy som vložil nejaké zdrojáky, aby ste si to mohli bezpracne vyskúšať. Je tam aj jeden projektík, ale scompilovaný na linuxe. Nie je však problém scompilovať ho ja na win.

Ak máte výhrady, iné skúsenosti, poprípade nejaké nápady na vylepšenie článku, napíšte do komentárov, budem sa tešiť. ;-)

Kurz programovania v jazyku C pokračuje Prácou so súbormi.


 

Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.

Stáhnout

Stažením následujícího souboru souhlasíš s licenčními podmínkami

Staženo 44x (17.56 kB)
Aplikace je včetně zdrojových kódů v jazyce c

 

Předchozí článek
Testování v jazyce C pokračování
Všechny články v sekci
Pokročilé konstrukce jazyka C
Článek pro vás napsal Libor Šimo (libcosenior)
Avatar
Uživatelské hodnocení:
5 hlasů
Obľúbil som si jazyk C a snažím sa ho spoznať čo najhlbšie. Začal som používať c# WPF, je to krása.
Aktivity