Hlavní
Hlavní strana
Seznam článků
Nejčtenější články
Progres e-mailem
Visual C++ FAQ

Seriály
COM
ISAPI

banner2.gif (2546 bytes)

Nenechte si ujít
Neobdélníková okna
Tisk bez Preview
MFC a DLL
Logo v MDI ploše
Kouzla s kombo-boxem
Výjimky v C++

banner.gif (3305 bytes)

Proměnný počet parametrů funkce Radek Pavienský
20.10.1999
[Hlavní stránka]  |  [Rubrika]

Jazyk C umožňuje vytvářet funkce s proměnným počtem parametrů, což je dost neobvyklá vlastnost. V tomto článku si ukážeme na nebezpečí, která na nás číhají při programování a používání funkí s proměnným počtem parametrů.

Nejprve si položme otázku, k čemu vlastně vytvářet funkce s proměnným počtem parametrů. V některých chvílích je totiž výhodné vytvořit funkci, jež nemá dán ani počet ani typy předávaných parametrů a toto odložit až na okažik volání funkce. Typickým příkladem může být funkce printf a její varianty. Tato funkce očekává jako první parametr formátovací řetězec a další parametry jsou volitelné, což nám umožňuje používat funkci velmi volným způsobem. To se může zdát jako výhodné a šikovné (no uvidíme později).

Před tím, než se podíváme na zmiňovaná nebezpečí, zopakujme si jak vytvářet a používat funkce s proměnným počtem parametrů.

Pokud chceme mít funkci s proměnným počtem parametrů, uvedeme v seznamu parametrů místo parametru tři tečky, které označují, že další parametry jsou libovolného typu a je jich libovolný počet. Proměnný počet parametrů musí být uveden vždy jako poslední položka v seznamu formálních parametrů a funkce nesmí obsahovat pouze položku proměnný počet parametrů. Pro přístup k parametrům se používají makra, která jsou definována v hlavičkovém souboru STDARG.H a jedná se o následující:

va_list Definice proměnné představující seznam parametrů.
va_start Nastaví adresu, kde začíná seznam parametrů.
va_end Ukončí práci se seznamem parametrů.
va_arg Získá další parametr ze seznamu parametrů.

Proměnná představující seznam parametrů je ve skutečnosti obyčejný ukazatel do paměti zásobníku, který se nastaví pomocí makra va_start. Makro va_end naopak nastaví ukazatel na NULL. Makro va_arg posune ukazatel o počet bajtů podle předaného typu. Tedy nic složitého ani světoborného.

Jako jednoduchý příklad si ukažme funkci Kalkul, která bude pracovat jako sčítačka celočíslených parametrů. Tedy třeba takto:

int Kalkul(int nCount, ...)
{
  // Definice ukazatele na parametry
  va_list pParams;
  // Inicializace ukazatele
  va_start(pParams, nCount);
  int nSum = 0;
  // Pro všechny parametry...
  while (nCount)
  {
    // Získání parametru a přesun na další parametr v seznamu
    nSum += va_arg(pParams, int);
    // 
    nCount--;
  }
  
  va_end(pParams);
    
  return nSum;
}

void main()
{
  // Volání funkce se čtyřmi volitelnými parametry
  int nSum = Kalkul(41234);  
}

Samozřejmě, že bychom mohli vymyslet inteligentnější příklad, ale dejme přednost jednoduchosti. Nyní se podívejme, jaká nebezpečí na nás číhají.

Problémem je, že se nikde nekontroluje počet a typy skutečných parametrů, můžeme tedy volat funkci Kalkul následovně (a chybně):

  // Příliš mnoho parametrů
  nSum = Kalkul(21234);
  // Málo parametrů
  nSum = Kalkul(312);
  // Parametry jsou jiného typu
  nSum = Kalkul(2"nic""moc");

Ve funkci Kalkul se prostě očekávají hodnoty typu int a je jedno jakého typu jsou předané parametry - funkce Kalkul je chápe jako by to byla celá čísla. Což samozřejmě vede k nesprávným výsledkům a v některých případech to může vést až k pádu aplikace.

Je to způsobeno tím, že proměnný počet parametrů je realizován pomocí ukazatele do zásobníku, který se posouvá na další hodnotu pomocí makra va_arg o daný počet bajtů. Podíváme-li se na třetí případ, tak zjistíme, že na zásobník jsou kromě hodnoty 2 předány ukazatele na řetězce. To ale ve funkci kalkul nemáme šanci zjistit, a protože očekáváme typ int, pracujeme ve skutečnosti s adresami řetězců. Výsledkem součtu tedy bude součet adres obou dvou řetězců.

Další problém nastává, pokud chceme předat hodnoty různých typů. Pak musíme jako první parametr předávat další informace o parametrech (nejen počet jako v Kalkul). Dobrým příkladem pak může být funkce printf a její formátovací řetězec.

Jak je vidět, tak nevýhody výrazně převyšují výhody, a proto si myslím, že proměnný počet parametrů je sice zajímavá vlastnost jazyka C, ale spíše teoreticky a v praxi bychom ji měli používat pouze v nejnutnějších případech, kdy si nelze pomoc jinak.

A na samotný závěr malá otázečka (řešení je v komentářích ke článku):

// Proč to padá?
int x = 0;
scanf("%d", x);

Podobné články:

Kdo Otázka nebo připomínka
paviensky@eternal.cz Odpověď na otázku

Prohlížení příspěvků nebo nový příspěvek

O firmě... Kontakt Ostatní