Seminář 6

Variadické funkce

Variadické funkce jsou funkce s proměnným počtem parametrů. Variadické funkce mají povinné argumenty, těch je v každém zavolání stený počet, a volitelné argumenty, jejichž počet se může měnit. Každá variadická funkce musí mít alespoň jeden povinný argument.

Zjištění počtu a typu volitelných argumentů uvnitř funkce se neděje automaticky, řeší se konvencí. Existují v zásadě tří přístupy.

V prvním předpokládáme pevný typ volitelných argumentů, v povinném argumentu předáváme jejich počet.

//hlavicka
float sum_of_floats(int count, ...);  // hlavicka

// volani
sum_of_floats(3, 1.2, 2.2, 3.3);  // vysledek je 6.6
sum_of_floats(2, 2.1, 3.3);       // vysledek je 5.4

Ve druhém také předpokládáme pevný typ argumentů, ale poslední nepovinný argument má speciální hodnotu, například 0.

//hlavicka
int sum_of_ints(int first, ...);  // hlavicka

// volani
sum_of_ints(1, 2, 3, 0);  // vysledek je 6
sum_of_ints(2, 2, 0);     // vysledek je 4

Ve třetím přístupu předáme informaci o typech i počtu volitelných argumentů pomocí povinných argumentů, příkladem takové funkce je printf.

K nepovinným argumentům přistupujeme pomocí maker z stdarg.h.

Podrobnosti v následujících příkladech.

float sum_of_floats(int n, ...) {

    va_list args;

    float ret = 0.0;

    va_start(args, n);  // tady se musi predat va_list a posledni povinny argument 
    for (int i = 0; i < n; i += 1) {
        ret += va_arg(args, double); // tady se musi predat va_list typ argumentu
    }

    va_end(args);  // ukonceni prace s argumenty

    return ret;
}

//
// nutno volat aspon dvema argumenty !!!
// jinak UB
//
int sum_of_ints(int first, ...) {

    va_list args;

    int ret = first;

    va_start(args, first);

    while (1) {
        int val = va_arg(args, int);
        if (!val) break;
        ret += val;
    }
    
    va_end(args);

    return ret;
}

void almost_printf(char *format, ...) {

    va_list args;

    int index = 0;

    va_start(args, format);
  
    while(format[index]) {
        switch(format[index]) {
        case 'i':
            printf("%i ", va_arg(args,int));
            break;
        case 'd':
            printf("%f ", va_arg(args,double));
        break;
        }
        index += 1;
    }

    va_end(args);
}

Úkoly

  1. Navhněte strukturu pro zlomek a upravte funkci almost_printf tak, aby tiskla i zlomky.

  2. Navhněte strukturu pro zlomek a naprogramujte variadickou funkci pro výpočet sumy komplexních čísel využívají prvního přístupu k určení počtu volitelných argumentů.

  3. Napište funkci long double prumer(char* format, ...), která výpočítá aritmetický průměr ze zadaných hodnot různých datových typů. Typy předávaných hodnot jsou určeny pomocí parametru format, který může tvořit libovolná posloupnost znaků odpovídající typům následujících parametrů - i pro typ int, d pro typ double a l pro long double.