以前の実装では、関数が期待するパラメータの型を指定できませんでした。しかし、ISO C でプロトタイプを使用すれば、これを指定できます。printf() などの関数をサポートするために、プロトタイプの構文では特別な省略記号 (…) が終了を示す記号として使用されます。 実装によっては可変引数を処理するために特別なことを行う必要があるため、ISO C では、すべての宣言とこのような関数などの定義が末尾に省略記号を含むべきであると規定しています。
パラメータの「…」部分には名前がないため、stdarg.h に含まれている特別なマクロにはこれらの引数へアクセスするための関数が含まれています。初期のバージョンではこのような関数は varargs.h に含まれている同様なマクロを使用しなければいけませんでした。
次に、これから書こうとする関数が errmsg() というエラーハンドラであると仮定します。この関数は void を返し、固定パラメータとして、エラーメッセージの詳細を指定する int だけを持つと仮定します。このパラメータのあとには、ファイル名または行番号 (あるいは、その両方) を指定できます。そして、ファイル名または行番号のあとには、(printf() のような) エラーメッセージのテキストを指定する書式と引数を指定できます。
初期のコンパイラで前述の例をコンパイルするには、ISO C コンパイルシステム専用に定義されたマクロ __STDC__ を多く使用する必要があります。したがって、適切なヘッダーファイルにおける関数の宣言は次のようになります。
#ifdef __STDC__ void errmsg(int code, ...); #else void errmsg(); #endif |
errmsg() の定義を持つファイルは、古い形式と新しい形式を併用できます。まず、インクルードするヘッダーはコンパイルシステムによって異なります。
#ifdef __STDC__ #include <stdarg.h> #else #include <varargs.h> #endif #include <stdio.h> |
そのあとで fprintf() と vfprintf() を呼び出すため、stdio.h をインクルードしています。
次は関数の定義です。識別子 va_alist と va_dcl は古い形式の varargs.h インタフェースの一部です。
void #ifdef __STDC__ errmsg(int code, ...) #else errmsg(va_alist) va_dcl /* Note: no semicolon! */ #endif { /* more detail below */ } |
古い形式の変数引数メカニズムでは固定パラメータを指定できないため、固定パラメータは、可変部分の前でアクセスされるように配置しなければいけません。また、パラメータの「… 」部分に名前がないため、新しい va_start() マクロは 2 番目の引数 (「…」の直前にあるパラメータの名前) を持ちます。
拡張として、Sun ISO C では、固定パラメータなしで関数を宣言および定義できます。
int f(...);
このような関数の場合、va_start() は 2 番目の引数を空にして呼び出す必要があります。
va_start(ap,)
次は関数の本体です。
{ va_list ap; char *fmt; #ifdef __STDC__ va_start(ap, code); #else int code; va_start(ap); /* extract the fixed argument */ code = va_arg(ap, int); #endif if (code & FILENAME) (void)fprintf(stderr, "\"%s\": ", va_arg(ap, char *)); if (code & LINENUMBER) (void)fprintf(stderr, "%d: ", va_arg(ap, int)); if (code & WARNING) (void)fputs("warning: ", stderr); fmt = va_arg(ap, char *); (void)vfprintf(stderr, fmt, ap); va_end(ap); } |
va_arg() と va_end() マクロは両方とも古い形式と ISO C バージョンで同様に動作します。va_arg() は ap の値を変更するため、vfprintf() への呼び出しを次のようにすることはできません。
(void)vfprintf(stderr, va_arg(ap, char *), ap); |
マクロ FILENAME、LINENUMBER、および WARNING の定義は、おそらく、errmsg() の宣言と同じヘッダーに含まれています。
errmsg() への呼び出しの例は次のようになります。
errmsg(FILENAME, "<command line>", "cannot open: %s\n", argv[optind]); |