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;
va_list aq;
*aq = *ap;
va_list aq;
va_copy(aq, ap);
...
va_end(aq);
歴史的なセットアップは以下のとおりである。
#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
マクロに '{' を含み、
va_end
マクロに対応する '}' を含むシステムもあるので、
この二つのマクロは同じ関数になければならない。
#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 's': /* string */
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 */
/* 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);
}