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);
呼び出される関数では、 va_list 型のオブジェクトが宣言されていなければならない。このオブジェクトが va_start(), va_arg(), va_end() の各マクロによって扱われる。
引き数 last は引き数リストのうち、可変な部分の直前に置かれる引き数の名前であ る。つまり呼び出された関数が型を知っている最後の引き数である。
この引き数はレジスタ変数や関数、配列として 宣言してはならない。この引き数のアドレスが va_start() マクロで用いられるかもしれないからである。
va_start() マクロの直後に va_arg() を最初に実行すると、 last の次の引き数が返る。続けて実行すると、残りの引き数がそれぞれ返る。
次の引き数がなかったり、 type が次の引き数の実際の型と互換でない場合 (デフォルトの引き数変換で扱 えなかった場合) には、予測できないエラーが起こる。
ap が va_arg(ap,type) の形で関数に渡されると、 ap の値は関数から返って来た後は不定となる。
va_list aq = ap;残念ながら、(長さ 1の)ポインタの配列として扱うシステムもある。 そのような場合、以下のようにする必要がある。
va_list aq; *aq = *ap;最後に、引き数をレジスタで渡すシステムの場合、 va_start() でメモリを割り当て、引き数を格納し、 次の引き数がどれかを指し示すようにする必要がある。 そして va_arg() でリストを順番にたどり、 va_end() で割り当てたメモリを開放する。 このような状況に対応するため、C99 では va_copy() マクロを追加し、 前述のような割り当ては以下のように置き換えられるようにした。
va_list aq; va_copy(aq, ap); ... va_end(aq);va_copy() が実行されるごとに、 対応する va_end() を同じ関数内で実行しなければならない。 この名前はまだ draft proposal なので、 va_copy() の代わりに __va_copy を用いるシステムもある。
歴史的なセットアップは以下のとおりである。
#include <varargs.h> void foo(va_alist) va_dcl { va_list ap; va_start(ap); while (...) { ... x = va_arg(ap, type); ... } va_end(ap); }va_start マクロに aq}aq を含み、 va_end マクロに対応する aq{aq を含むシステムもあるので、 この二つのマクロは同じ関数になければならない。
#include <stdio.h> #include <stdarg.h> void foo(char *fmt, ...) { va_list ap; int d; char c, *s; va_start(ap, fmt); while (*fmt) switch (*fmt++) { case aqsaq: /* string */ s = va_arg(ap, char *); printf("string %s\n", s); break; case aqdaq: /* int */ d = va_arg(ap, int); printf("int %d\n", d); break; case aqcaq: /* char */ /* need a cast here since va_arg only takes fully promoted types */ c = (char) va_arg(ap, int); printf("char %c\n", c); break; } va_end(ap); }