コンテンツにスキップ

利用者: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)コピーがva1va2

関数に渡される名前のない引数の数またはタイプを決定するために定義されたメカニズムはありません。関数は、これを何らかの方法で認識または決定するために必要なだけであり、その手段はさまざまです。一般的な規則は次のとおりです。

  • 引数の型を示す指定子が埋め込まれたprintfまたはscanf
  • 可変個引数の最後の番兵の値。
  • 可変個引数の数を示すcount引数。

名前のない引数を他の呼び出しに渡す[編集]

名前のない引数リストのサイズは一般に不明であるため(ほとんどのコンパイラで採用されている呼び出し規約では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と混在させることはできません。

参考文献[編集]

 

  1. ^ IEEE Std 1003.1 stdarg.h”. 2009年7月4日閲覧。
  2. ^ Single UNIX Specification varargs.h”. 2007年8月1日閲覧。