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 mezi voláními může lišit. 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ři 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
double sum_of_doubles(int count, ...);  // hlavicka

// volani
sum_of_doubles(3, 1.2, 2.2, 3.3);  // vysledek je 6.6
sum_of_doubles(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.

double sum_of_doubles(int n, ...) 
{
    va_list args;
    double ret = 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);
}

Pozor, při předávání variadických argumentů dochází ke automatické konverzi typů: float je konvertován na double, char a short jsou konvertovány na int, jejich bezznaménkové varianty na unsigned int.

Úkoly

  1. Navhněte strukturu pro komplexní číslo 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ů.

  2. 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.

  3. Naprogramujte vlastní verzi funkce printf určenou pro tisk do string_builder z druhého bonusového úkolu. (Vyberte rozumnou část formátovacích primitiv, kterou chcete podporovat). Hlavička funkce je tedy například

    int sb_printf(char *format, ...)