Fotografický magazín "iZIN IDIF" každý týden ve Vašem e-mailu.
Co nového ve světě fotografie!
Zadejte Vaši e-mailovou adresu:
Kamarád fotí rád?
Přihlas ho k odběru fotomagazínu!
Zadejte e-mailovou adresu kamaráda:
C/C++
Učíme se C (24. díl) - Funkce s proměnným počtem parametrů
ucime se jazyk C
25. dubna 2001, 00.00 | Definice funkcí s proměnným počtem parametrů, přístup k těmto
parametrům a makra hlavičkového souboru stdarg.h.
Zatím jsme při programování v C byli zvyklí, že při volání funkce musí počet jejich skutečných parametrů odpovídat počtu parametrů formálních, tedy těch, které jsme určili v definici funkce. Ale kromě tohoto způsobu nabízí jazyk C i možnost definovat funkce, jejichž počet skutečných parametrů může být proměnný, což je nesporná výhoda oproti jazykům, které takovouto možnost nepodporují.
Definice funkce s proměnným počtem parametrů
Definice funkcí s proměnným počtem parametrů se od definicí normálních funkcí příliš neliší. Skutečnost, že funkce může mít různý počet skutečných parametrů dáme překladači najevo znaky tří teček na místě posledního parametru.
typ identifikátor_funkce(seznam_pevných_parametrů, ...)
Seznam pevných parametrů nelze vynechat, z čehož vyplývá, že každá funkce s proměnným počtem parametrů musí mít minimálně jeden pevný parametr. O něco níže se dozvíme, k čemu je vlastně dobrý.
Jako příklad si můžeme uvést hlavičku funkce pro součet libovolného počtu čísel.
int secti(unsigned int a, ...)
Asi se teď ptáte, jak vlastně lze k dodatečným parametrům z těla funkce přistoupit. Pro lepší pochopení celé problematiky bude lepší trochu blíže se podívat na konvence volání funkcí v jazyce C.
Konvence volání funkcí v jazyce C
To co nás bude hlavně zajímat, je způsob předávání parametrů. V okamžiku, kdy je Céčkovská funkce zavolána, hodnoty jejích skutečných parametrů jsou přeneseny do zásobníku, a to v opačném pořadí, než jak jsou při volání zapsány. Nejprve se tedy uloží poslední parametr. První argument funkce přijde na řadu až jako poslední, a je tedy až na vrcholu zásobníku. Nad ním jsou pak ještě vytvořeny lokální proměnné funkce. Důležité ale je, že překladač dokáže v zásobníku identifikovat všechny pevné parametry a přiřadit jim příslušné identifikátory. V jazycích jako je Pascal se skutečné parametry ukládají do zásobníku v pořadí jak jsou zapsány, takže při implementaci funkcí s proměnným počtem parametrů by mezi pevnými parametry (které by ležely někde níže v zásobníku) a lokálními proměnnými funkce (vrchol zásobníku) byla ještě oblast dat(dodatečné parametry), o které by nebylo známo jak je velká.
Každopádně, v C už máme přiřazeny identifikátory pevným parametrům a řešíme problém jak pracovat s daty předanými zbylými parametry. Opět je to část zásobníku, o které nevíme jak je velká. A právě teď nám poslouží pevné parametry, protože právě těmi programátor předává do těla funkce zprávu o tom, kolik a jaké nepovinné parametry ještě funkci předává. V případě naší funkce secti() bychom museli prvním parametrem předat počet dalších parametrů. Pokud bychom tedy chtěli sečíst čísla 3, 15, 6, a 7, zavolali bychom funkci takto: secti(4, 3, 15, 6, 7); První parametr, hodnota 4, tedy udává počet dodatečných parametrů.
Jiným příkladem může být například funkce printf(), která má také pouze jeden pevný parametr, řetězec, po jehož zanalyzování (zjištění počtu a typů popisovačů) je jasné, s kolika skutečnými parametry byla funkce volána a podle toho se pak řídí její další průběh.
S tím co už víme, můžeme konečně sestrojit tělo funkce secti():
|
Nejdůležitější částí kódu je inicializace pointeru p, který musí ukazovat za poslední pevný parametr, tedy na první přebývající parametr. Pak už je tento ukazatel v těle cyklu for jenom posouván na další parametry, které jsou přičítány k výsledné hodnotě soucet. V našem příkladu to máme jednodušší o to, že funkce secti() nepočítá s jinými parametry než typu int. Asi ale vidíte určité riziko, které je s funkcemi s proměnným počtem parametrů spojeno. Překladač totiž nemůže provádět typovou kontrolu, a proto se při volání funkce s nesprávným počtem či typem parametrů mohou vyskytnout chyby ve funkčnosti programu.
Makra hlavičkového souboru stdarg.h
Norma ANSI jazyka C pro zjednodušení a kvůli zajištění přenositelnosti (zásobník nemusí být na všech platformách implementován stejně) zavádí soubor stdarg.h, ve kterém jsou definována užitečná makra pro práci s proměnným počtem parametrů. Zavádí například datový typ va_list, což je vlastně jen obyčejný void ukazatel. Pro jeho inicializaci použijeme makro va_start, které tento ukazatel nastaví na začátek seznamu přebývajících parametrů.
va_start(p, lastparam)
p - ukazatel typu va_list, který bude inicializován
lastparam - poslední formální parametr funkce
Pomocí opakovaného volání makra va_arg (makro posouvá ukazatel p) získáme
postupně hodnoty jednotlivých přebývajících parametrů.
va_arg(p, type)
p – již inicializovaný ukazatel typu va_list
type – identifikátor typu získávaného parametru.
Po přečtení všech parametrů bychom měli ukazatel p ještě deinicializovat
pomocí makra va_end, které přiřadí pointeru p hodnotu NULL.
va_end(p)
Naše funkce secti() by tedy s použitím maker ze stdarg.h vypadala asi nějak takto:
|
Obsah seriálu (více o seriálu):
- Učíme se C (1. díl)
- Učíme se C (2. díl)
- Učíme se C (3.díl)
- Učíme se C (4. díl)
- Učíme se C (5.díl)
- Učíme se C (6.díl)
- Učíme se C (7.díl)
- Učíme se C (8.díl)
- Učíme se C (9.díl) - Řetězce
- Učíme se C (10.díl) - Vstupně výstupní funkce
- Učíme se C (11.díl) - Formátovaný výstup
- Učíme se C (12.díl) - Formátový specifikátor
- Učíme se C (13.díl)
- Učíme se C (14.díl) - Vícerozměrná pole
- Učíme se C (15.díl) - Ukazatele
- Učíme se C (16.díl) - Modifikátory paměťové třídy
- Učíme se C (17.díl) - Pole a ukazatel
- Učíme se C (18. díl) - Dynamická alokace paměti
- Učíme se C (19. díl) - Práce se soubory I.
- Učíme se C (20. díl) - Práce se soubory II.
- Učíme se C (21. díl) - Parametry funkce main()
- Učíme se C (22. díl) - Ukazatele na funkce
- Učíme se C (23. díl) - Složité deklarace a definice
- Učíme se C (24. díl) - Funkce s proměnným počtem parametrů
- Učíme se C (25. díl) - Bitové operátory a bitové pole
- Učíme se C (26. díl) - Datové typy enum a union
- Učíme se C (27. díl - závěr) - Programové moduly
-
25. listopadu 2012
-
30. srpna 2002
-
10. října 2002
-
4. listopadu 2002
-
12. září 2002
-
25. listopadu 2012
-
28. července 1998
-
31. července 1998
-
28. srpna 1998
-
6. prosince 2000
-
27. prosince 2007
-
4. května 2007