多分、C 言語で最初に習う関数って十中八九 "printf" だと思うのですが、 「んじゃ printf 実装してみて」って言われるとさて困る。引数の個数変わるやん。 ちうわけで、これの実装法について勉強してみたわけです。
まず、受け取る側のプロトタイプは、
int function( int arg1, int arg2, ... );
という感じ。"..." の部分には変数がいくつ入っても (0 個でも) OK ということになります。
なお、"..." は必ず最後に置き、かつそれ以外に最低 1 つ引数が必要になるようです。
ンなモン要らんという場合は、ダミーの変数を用意しておけばいいでしょう。
なお、ダミーの変数を使ったときに Warning が出て困るという場合は、
arg1 = arg1;
とかしておくとこれを抑制できたような気がします。姑息だけど。
実体側では、まず va_list 型の変数を一つ宣言しておきます。LSI C のドキュメントでは
ap という変数名を使っていましたのでこれを採用します。
次に、関数 (正確にはマクロ) va_start() を呼びます。引数は 2 つで、
第 1 引数には先ほど宣言した va_list 型の変数を、第 2 変数には "..."
の一つ前の引数を渡します。これで、変数 ap が初期化されます。
va_list ap;
va_start( ap, arg2 );
さて、実際に変数を引っ張り出すには、関数 va_arg() を呼びます。
type va_arg( va_list ap, type );
この "type" ってのは C++ なんかで割と目にするモノなのですが、 ここには変数の型名を書いてやります。例えば、"int" としてやると、 渡されている変数を int 型として引っ張り出してくれます。
細かいことを言うなら、ap ってのは引数に対するディスクリプタで、va_arg() は ap の指す変数を type 型に変換して返し、ap の指す先を次の変数に移す、ということをやっている、のだと思います。
最後に、ap を使い終わったら関数 va_end() を呼びます。
void va_end(va_list ap);
なお、引き出す変数がなくなった時には、va_arg は NULL を返します。 ただ、int で取り出した数が本当に 0 だったときの判定法が見つかりません。 errorno にも変化はないし……。
実際の例としては、こんな感じ。
/* 渡された文字列を順々に表示 */
void mltputstr( int dummy, ... )
{
va_list ap;
char *str;
va_start( ap, dummy );
while( ( str = va_arg( ap, char * ) ) != NULL )
puts( str );
dummy = dummy /* Warning を抑制 */
va_end( ap );
}
実際考えてみると、こういうコードを組むことってそんなに多くないのかも。 ルーチンの側から利用できるわけではないんだし……。う〜む。