Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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í stejný 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.

  • 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
    
  • 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
    
  • Informaci o typech i počtu volitelných argumentů předáme pomocí povinných argumentů. Tento přístup používá například funkce printf.

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

  • typ va_list pro udržování nezpracovaných argumentů,
  • va_start nastaví va_list na všechny volitelné argumenty,
  • va_arg vrátí hodnotu prvního argumentu ve va_list, potřebuje znát typ, první argument odstraní z va_list.
  • va_end ukončí práci s nepovinnými argumenty.

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 a 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);
}

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, která s podporou tiskových direktiv %i, %f a %z, kde poslední direktiva vede k tisku zlomku. Při řešení lze využít funkci printf.