STDARG(3) | Linux Programmer's Manual | STDARG(3) |
stdarg, va_start, va_arg, va_end, va_copy - 個数、型が可変な引数リスト
#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);
関数は呼び出しに際して、個数や型が可変な引数をとることができる。 インクルードファイル <stdarg.h> では va_list 型が宣言されており、3 つのマクロが定義されている。これらを用いると、 呼び出された関数側では個数や型を知らない引き数のリストを、順に一 つづつ読み込むことができる。
呼び出される関数では、 va_list 型のオブジェクトが宣言されていなければならない。このオブジェクトが va_start(), va_arg(), va_end() の各マクロによって扱われる。
va_start() マクロは最初に呼び出さなければならない。これは ap を初期化し、 va_arg() と va_end() で用いることができるようにする。
引き数 last は引き数リストのうち、可変な部分の直前に置かれる引き数の名前であ る。つまり呼び出された関数が型を知っている最後の引き数である。
この引き数はレジスター変数や関数、配列として 宣言してはならない。この引き数のアドレスが va_start() マクロで用いられるかもしれないからである。
va_arg() マクロは、呼び出し時に指定された引き数のうち、 次の位置にあるものを指定した型 type の値として取得する。 引き数 ap は va_list ap で、 va_start() によって初期化されている必要がある。 va_arg() を呼び出すごとに ap は変更され、次回の呼び出しの際に、さらに次の引き数を返すようになる。 引き数 type は型の名前である。 type の前に * を付ければ、オブジェクトへの型付きポインターが得られる。
va_start() マクロの直後に va_arg() を最初に実行すると、 last の次の引き数が返る。続けて実行すると、残りの引き数がそれぞれ返る。
次の引き数がなかったり、 type が次の引き数の実際の型と互換でない場合 (デフォルトの引き数変換で扱 えなかった場合) には、予測できないエラーが起こる。
ap が va_arg(ap,type) の形で関数に渡されると、 ap の値は関数から返って来た後は不定となる。
va_start() が実行される毎に、同じ関数内で対応する va_end() が実行されなければならない。 va_end(ap) が呼び出された後、変数 ap の値は不定となる。 va_start() と va_end() の組を何回も並べて使うことも可能である。 va_end() はマクロかもしれないし関数かもしれない。
va_copy() マクロは (初期化済みの) 可変長引き数リスト src を dest にコピーする。動作は、 last 引き数に dest を渡して va_start() を dest に適用し、それから src が現在の状態に達するまでに呼び出したのと同じ回数だけ va_arg() を呼び出す、のと同じことを行う。
すぐ分かる va_list
の実装は、variadic
な関数のスタックフレームのポインターである。
このような場合(ほとんどはそうである)、
単に以下のようにすればいいように思える。
va_list aq = ap;
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_arg(), va_end(), va_copy() はスレッドセーフである。
va_start(), va_arg(), va_end() マクロは C89 準拠である。 va_copy() は C99 で定義されている。
これらのマクロは、以前から用いられてきた同等のマクロ群と 互換ではない。過去のものと互換なバージョンは、 インクルードファイル <varargs.h> に存在する。
歴史的なセットアップは以下のとおりである。
#include <varargs.h> void foo(va_alist)
va_dcl {
va_list ap;
va_start(ap);
while (...) {
...
x = va_arg(ap, type);
...
}
va_end(ap); }
varargs マクロとは異なり、 stdarg マクロでは固定引き数なしで関数を指定することが許されていない。 これは varargs ベースのコードを stdarg のコードに書き換えるときに、面倒な作業のもとになる。 また、すべての引き数を va_list として可変個指定したいような場合 (vfprintf(3) など) にも障害となる。
関数 foo は書式文字からなる文字列を受け入れ、その書式文字に対応する型で可変個の 引き数を読み込み、印字する。
#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); }
この man ページは Linux man-pages プロジェクトのリリース 3.79 の一部 である。プロジェクトの説明とバグ報告に関する情報は http://www.kernel.org/doc/man-pages/ に書かれている。
2013-12-10 |