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 の実装は、variadic な関数のスタックフレームのポインターである。 このような場合(ほとんどはそうである)、 単に以下のようにすればいいように思える。
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 を用いるシステムもある。
インターフェース | 属性 | 値 |
va_start(), va_end(), va_copy() | Thread safety | MT-Safe |
va_arg() | Thread safety | MT-Safe race:ap |
#include <stdio.h> #include <stdarg.h>
void foo(char *fmt, ...) /* '...' is C syntax for a variadic function */
{
va_list ap;
int d;
char c;
char *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);
}