このヘッダの目的
<stdarg.h>は、可変長引数の関数を実装する。
定義済み識別子
働き
va_list
型は、 va_start 、 va_arg 、
va_end マクロで使われる型である。
解説
Cでは、任意の型の可変数の引数をとる関数を定義することができる。 これはどのような関数かというと、 printf() や scanf() などのように、いろいろな型の引数を好きなだけつけられるという関数である。 プログラマがこのような関数を定義するときの注意するべき点としては、コンパイラの型チェック機能が可変数引数には活かされないということがある。
例えば、分かりやすい例で見ると、
#include <stdio.h> void main(void){ int i = -100; printf("%u", i); }
このソースはコンパイルエラーを発生させないが、実行結果が期待通りになる処理系はまずないだろう。 printf 関数が、引数の型は unsigned int だと思っているからである。
ちなみに、以下のコードは期待通りに動くことが規格によって保証されている。
#include <stdio.h> void main(void){ unsigned u = 100; printf("%d", u); }
整数の内部表現に関わらず、符号ありとなしで必要な記憶域は同じであり、符号ありの整数の正の部分では符号なしと同じ内部表現になることが保証されているので、
これを例えば printf("%d", (int)u);
とか printf("%u", u);
と書き直さなくても、処理系が規格に従っていれば問題は発生しない。
余談だが、C++の型変換演算子オーバーライドも可変長引数では動いてくれないので、 さるクラスライブラリの文字列クラスは、 printf() ファミリーの関数にキャストなしの引数として渡しても置換されるように、 唯一のメンバであるポインタをバッファに向け、サイズその他の文字列属性はポインタより前に置く(アドレス減算で参照する)という、 苦しい工夫をしている。
可変数引数の関数は、少なくとも一つの固定された引数を最初にとらなければならない。 関数の中で、これらの固定された引数から可変部分がどのような型かを決めるので、呼び出し側は不変部分と可変部分の整合性を保つ必要がある。
可変数引数の関数の宣言は次のように、最後に...をつける。
void varargfunc(char* str, int count, ...);
また、引数の宣言を完全に省略した形もある。これは仮引数に何も書かない(例: void func();
)ことで使えるが、
グローバル変数などを使わない限り、引数の型を外部から知ることができない以上、古いコードとの互換性を保つ以外に殆ど意味のない書き方である。
引数を取らないことを宣言するには、 void func(void);
というふうに宣言するのが望ましい。
va_list 型のオブジェクトは、 v で始まるライブラリ関数に渡すことができる。 これらの関数は vprintf 、 vfprintf 、 vsprintf の三つである。