STDARG(3) Podręcznik programisty Linuksa STDARG(3)
stdarg - listy zmiennych argumentów
#include <stdarg.h>
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
Funkcję można wołać z różną liczbą argumentów, różnych typów. Plik
nagłówkowy stdarg.h deklaruje typ va_list i definiuje trzy makra,
iterujące poprzez listę argumentów, których liczba i typy nie są znane
wywołanej funkcji.
Wywołana funkcja musi zadeklarować obiekt typu va_list, który jest
używany przez makra va_start, va_arg i va_end.
va_start
Makro va_start inicjuje ap do dalszego użytku przez va_arg i va_end, i
musi być wywołane jako pierwsze.
Parametr last jest nazwą ostatniego parametru przed zmienną listą argu-
mentów, czyli ostatnim parametrem, którego typ był funkcji znany.
Ponieważ adres tego parametru jest używany w makrze va_start, nie
powinien on być deklarowany jako zmienna rejestrowa, funkcja czy typ
tablicowy.
va_arg
Makro va_arg rozwija się do wyrażenia, które ma typ i wartość
następnego argumentu w wywołaniu. Parametr ap to va_list ap
zainicjowany przez va_start. Każde wywołanie va_arg zmienia ap tak, że
następne wywołanie zwraca następny argument. Parametr type Jest nazwą
typu, podaną tak że typ wskaźnika do obiektu, który ma podany typ można
uzyskać przez dodanie * do type.
Pierwsze użycie makra va_arg po va_start zwraca argument za last(ostat-
nim). Kolejne wywołania zwracają wartości pozostałych argumentów.
Jeśli nie ma następnego argumentu lub jeśli type nie jest zgodny z
rzeczywistym typem następnego argumentu, pojawią się losowe błędy.
Jeśli ap zostanie przekazane do funkcji używającej va_arg(ap,type), to
wartość ap po zakończeniu tej funkcji będzie nieokreślona.
va_end
Każdemu wywołaniu va_start musi odpowiadać wywołanie va_end w obrębie
tej samej funkcji. Po wywołaniu va_end(ap) wartość ap będzie
nieokreślona. Lista może być przetwarzana wielokrotnie, przy czym każde
przetworzenie musi być zawarte pomiędzy va_start a va_end. va_end może
być zarówno makrem, jak i funkcją.
va_copy
Oczywista implementacja zawierałaby wskaźnik va_list do ramki stosu
funkcji o zmiennej liczbie argumentów. Przy takiej konfiguracji (jak
dotąd, najpowszechniejszej) nie ma żadnych przeciwskazań wobec podstaw-
ienia
va_list aq = ap;
Niestety, są również systemy, które robią to poprzez tablicę wskaźników
(o długości 1) i wtedy niezbędne jest
va_list aq;
*aq = *ap;
Wreszcie, w systemach, które przekazują parametry w rejestrach, może
okazać się koniecznym przydzielenie pamięci przez va_start, prze-
chowanie tam parametrów, jak też wskazań, który parametr jest następny,
tak aby va_arg mogło przejść całą listę. Wówczas va_end może wreszcie
zwolnić przydzieloną w tym celu pamięć. Aby dostosować się do tej
sytuacji, C99 dodaje makro va_copy, tak aby powyższe przypisanie mogło
byc zastąpione przez
va_list aq;
va_copy(aq, ap);
...
va_end(aq);
Każdemu wywołaniu va_copy musi odpowiadać wywołanie va_end w obrębie
tej samej funkcji. Niektóre systemy nie udostępniające va_copy posi-
adają zamiast tego __va_copy, gdyż ta nazwa była używana w szkicowej
propozycji standardu.
Funkcja foo pobiera łańcuch znaków formatujących i wypisuje argumenty z
nimi związane w oparciu o typ argumentu.
#include <stdio.h>
#include <stdarg.h>
void foo(char *fmt, ...) {
va_list ap;
int d;
char c, *p, *s;
va_start(ap, fmt);
while (*fmt)
switch(*fmt++) {
case 's': /* napis */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* występuje tu potrzeba rzutowania, gdyż va_arg
pobiera w pełni awansowane typy */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
Makra va_start, va_arg, i va_end są zgodne z ANSI X3.159-1989
(``C89''). C99 definiuje makro va_copy.
Makra te nie są zgodne z historycznymi makrami, które zastąpiły. Zgodna
wstecznie wersja znajduje się w pliku nagłówkowym varargs.h.
Historyczna konfiguracja to:
#include <varargs.h>
void foo(va_alist) va_dcl {
va_list ap;
va_start(ap);
while(...) {
...
x = va_arg(ap, type);
...
}
va_end(ap);
}
W niektórych systemach, va_end zawiera zamykający '}' odpowiadający '{'
w va_start, tak żeby obydwa makra musiały wystąpić w tej samej funkcji
w dozwolony sposób.
W przeciwieństwie do makr varargs, makra stdarg nie zezwalają pro-
gramistom na tworzenie funkcji bez ustalonych argumentów. Problem ten
powoduje utrudnienia podczas konwersji kodu varargs na kod stdarg, a
także utrudnia tworzenie funkcji, które mają za zadanie jedynie
przekazać wszystkie swoje argumenty do funkcji pobierającej argument
va_list, takiej jak vfprintf(3).
2001-10-14 STDARG(3)