Oracle® Solaris Studio 12.4: C ユーザーガイド

印刷ビューの終了

更新: 2014 年 12 月
 
 

6.2 可変引数を持つ関数

以前の実装では、関数が期待するパラメータの型を指定できませんでしたが、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_alistva_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 番目の引数 (「」ターミネータの直前にあるパラメータの名前) を持ちます。

拡張として、Oracle Solaris Studio 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);

マクロ FILENAMELINENUMBER、および WARNING の定義は、おそらく、errmsg() の宣言と同じヘッダーに含まれています。

errmsg() の呼び出し例を次に示します。

errmsg(FILENAME, "<command line>", "cannot open: %s\n",
argv[optind]);