利用者:KusaReMKN/Stdarg.h
stdarg.h は、C 言語で可変長引数を実現するための標準ライブラリのヘッダファイルである[1]。<It provides facilities for stepping through a a list of function arguments of unknown number and type.>。C++ で可変長引数を利用するには cstdarg を利用する。
stdarg.h は
stdarg.h
の内容は通常、可変個引数関数で使用されますが、可変個引数関数によって呼び出されるvprintf
)で使用される場合もあります。
可変個引数関数の宣言
[編集]可変長引数関数は任意の数の引数を取る関数であり、最後の引数の代わりに ...
を伴って宣言される。標準ライブラリにある printf 関数もこの同様に宣言されている。
可変個引数関数は、可変数の引数を取ることができる関数であり、最後のパラメーターの代わりに省略記号を使用して宣言されます。このような関数の例はprintf
です。典型的な宣言は
int check(int a, double b, ...);
可変個引数関数には、少なくとも1つの名前付きパラメーターが必要です。たとえば、
char *wrong(...);
Cでは許可されていません(C ++では、このような宣言は許可されています。 )Cでは、省略記号の前にコンマを付ける必要があります。 C ++では、これはオプションです。
可変個引数関数の定義
[編集]同じ構文が定義で使用されます。
long func(char, double, int, ...);
long func(char a, double b, int c, ...)
{
/* ... */
}
省略記号は、古いスタイルの関数定義には表示されない場合があります。
名前 | 説明 | 互換性 |
---|---|---|
va_list
|
引数を繰り返すためのタイプ | C89 |
stdarg.hマクロ
[編集]名前 | 説明 | 互換性 |
---|---|---|
va_start
|
va_list 引数の反復を開始します
|
C89 |
va_arg
|
引数を取得する | C89 |
va_end
|
va_list
|
C89 |
va_copy
|
va_list 内容を別のva_listにコピーします
|
C99 |
引数へのアクセス
[編集]名前のない引数にアクセスするには、可変個引数関数でva_list
型の変数を宣言する必要があります。次に、マクロva_start
が2つの引数で呼び出されます。1つva_list
型で宣言された変数で、2つ目は関数の最後に名前が付けられたパラメーターの名前です。この後、 va_arg
マクロを呼び出すたびに次の引数が生成されます。 va_arg
の最初の引数はva_list
で、2番目の引数は関数に渡される次の引数の型です。最後に、関数が戻る前に、 va_end
va_list
必要があります。 (すべての引数を読み取る必要はありません。 )。
C99 va_list
状態を複製できる追加のマクロva_copy
提供します。マクロ呼び出しva_copy(va2, va1)
コピーがva1
にva2
。
関数に渡される名前のない引数の数またはタイプを決定するために定義されたメカニズムはありません。関数は、これを何らかの方法で認識または決定するために必要なだけであり、その手段はさまざまです。一般的な規則は次のとおりです。
名前のない引数を他の呼び出しに渡す
[編集]名前のない引数リストのサイズは一般に不明であるため(ほとんどのコンパイラで採用されている呼び出し規約ではva_list
指す名前のない引数ブロックのサイズを決定できません)、信頼できる一般的な転送方法もありません。名前のない引数を別の可変個引数関数に入れます。引数リストのサイズを間接的な方法で決定できる場合でも(たとえば、 fprintf()
フォーマット文字列を解析することによって)、動的に決定された引数の数を内部の可変個引数呼び出しに渡すポータブルな方法はありません。このような呼び出しに渡される引数の数とサイズは、通常、コンパイル時に知る必要があります。この制限は、可変個引数関数の代わりに可変個引数マクロを使用することである程度緩和できます。さらに、ほとんどの標準ライブラリプロシージャは、名前のない引数リスト自体ではなく、名前のない引数リスト(つまり、初期化されたva_list
変数)への参照v
プレフィックス付きの代替バージョンを提供します。たとえば、 vfprintf()
は、実際の名前のない引数リストの代わりにva_list
を期待するfprintf()
代替バージョンです。したがって、ユーザー定義の可変個引数関数は、 va_start
を使用しva_list
変数を初期化し、それを適切な標準ライブラリ関数に渡すことができます。事実上、名前のない引数リストは、値ではなく参照によって渡されます。 Cでは名前のない引数リストを値で渡す信頼できる方法がないため、 va_list
を受け入れる同等の関数を提供せずに可変個引数API関数を提供することは、悪いプログラミング手法と見なされます。
型安全性
[編集]一部のC実装は、コンパイラがフォーマット文字列とセンチネルの適切な使用をチェックできるようにするC拡張機能を提供します。これらの拡張機能を除いて、コンパイラーは通常、渡された名前のない引数が関数が期待する型であるかどうかを確認したり、必要な型に変換したりすることはできません。したがって、タイプが一致しない場合は未定義の動作が発生するため、この点で正確性を確保するように注意する必要があります。たとえば、期待される型がint *
(int *)NULL
として渡される必要があります。 NULL
だけを書き込むとint
型またはvoid *
型の引数になりますが、どちらも正しくありません。もう1つの考慮事項は、名前のない引数に適用されるデフォルトの引数プロモーションです。 float
は自動的にdouble
プロモートされます。 int
よりも狭い型の引数はint
またはunsigned int
プロモートされます。名前のない引数を受け取る関数は、プロモートされた型を予期する必要があります。
GCCには、渡された引数をチェックする拡張機能があります。
例
[編集]#include <stdio.h>
#include <stdarg.h>
/* print all args one at a time until a negative argument is seen;
all args are assumed to be of int type */
void printargs(int arg1, ...)
{
va_list ap;
int i;
va_start(ap, arg1);
for (i = arg1; i >= 0; i = va_arg(ap, int))
printf("%d ", i);
va_end(ap);
putchar('\n');
}
int main(void)
{
printargs(5, 2, 14, 84, 97, 15, -1, 48, -1);
printargs(84, 51, -1, 3);
printargs(-1);
printargs(1, -1);
return 0;
}
このプログラムは次の出力を生成します。
5 2 14 84 97 15 84 51 1
関数内から他のvarargs関数(sprintfなど)を呼び出すには、関数のvar argバージョン(この例ではvsprintf)を使用する必要があります。
void MyPrintf(const char *format, ...)
{
va_list args;
char buffer[BUFSIZ];
va_start(args, format);
vsnprintf(buffer, sizeof buffer, format, args);
va_end(args);
FlushFunnyStream(buffer);
}
varargs.h
[編集]POSIXの古いバージョンではvarargs.h
定義されています。これは、Cの標準化以前のものであり、 stdarg.h
と同様の機能を提供します。このヘッダーは、ISOCにもPOSIXにも含まれていません。 2番目のバージョンで定義されているファイルを、単一のUNIX仕様は、単にC89ののすべての機能が含まれstdarg.h
その例外を除いて、:
- 標準のCの新しいスタイルの定義では使用できません
- 指定された引数は省略できます(標準Cでは少なくとも1つの引数が必要です)
インターフェースも異なります。 printargs
例では、代わりに次のように記述します。
#include <stdio.h>
#include <varargs.h>
/* There is no "void" type; use an implicit int return. */
printargs(arg1, va_alist)
va_dcl /* no semicolon here! */
{
va_list ap;
int i;
va_start(ap);
for (i = arg1; i >= 0; i = va_arg(ap, int))
printf("%d ", i);
va_end(ap);
putchar('\n');
return;
}
同じように呼ばれます。
varargs.h
は、実装の動作方法のために、古いスタイルの関数定義を必要とします。 [2]逆に、古いスタイルの関数定義をstdarg.h
と混在させることはできません。
参考文献
[編集]
- ^ “IEEE Std 1003.1
stdarg.h
”. 2009年7月4日閲覧。 - ^ “Single UNIX Specification
varargs.h
”. 2007年8月1日閲覧。