可変長引数

可変長引数とはプログラミング言語において、関数やマクロの引数が固定ではなく任意の個数となっている引数のことである。可変引数、可変個引数とも呼ばれる。

可変長引数を持つ関数を可変長引数関数と言う。

いくつかの言語では型安全が保証されなくなるので注意が必要である。

1. 手法
-----

可変長引数を取る手続きあるいは関数やメソッドは、多くのコンピュータプログラミング言語でサポートされている。

C言語 - 標準Cライブラリに用意されているstdarg.hを使う。仮引数に型の指定は特に行わない。
C# - 配列型の仮引数の直前にparamsキーワードを指定する。
Java - 仮引数の型名と仮引数名の間に ... を記述する。実引数は配列に格納され、型には制限が付く。J2SE 5.0で追加された機能。Javaの文法#可変引数を参照。
JavaScript - argumentsオブジェクトを利用する。
Perl - サブルーチンに固定の仮引数を指定することはできない。どんなサブルーチンでも引数はリスト変数 @_ を通して動的に受け取る以外に方法がない。
Python - 仮引数名の前に*あるいはを付けてアクセスする。
Common Lisp - &rest に続けてリストを受け取る仮引数名を指定する。
Scheme - . に続けてリストを受け取る仮引数名を指定する。

2. 例
----

2.1. C言語
--------

C言語においては printf、scanf といった標準ライブラリの関数が可変長引数をとる代表例である。可変長引数をとる関数のユーザー定義も可能である。信頼できない入力を扱う場合は書式文字列攻撃に注意する必要がある。可変長引数として渡す際に型情報が失われるため、型に関する情報を別途渡すなどする必要がある。

倍精度浮動小数点数の平均値を求める関数average_of_doublesの例を考える。

include
include /* stdarg.h には、マクロ va_start、va_arg、va_end が定義されている。対応する va_start と va_end の呼び出しは、同じ関数内でなければならない。 */

double average_of_doubles { va_list ap; double sum = 0; int i; assert; va_start; /* スタック変数のアドレスを参照するため、最後の引数が必要である。 */ for  { /* スタックに積まれているはずの double 型の値を取り出し、ポインタ変数 ap が次の引数を指すように変更する。 */ sum += va_arg; } va_end; return sum / count; }<WtXmlEndTag />

上記を呼び出すコード例は次の通りである。データの個数を最初の引数として与えている。

include int main { double average1 = average_of_doubles; double average2 = average_of_doubles; printf; printf; return 0; }<WtXmlEndTag />

Cの可変長引数には、「既定の実引数拡張」 と呼ばれる暗黙の型変換規則が適用される例えばCの国際規格ISO/IEC 9899:1999またはその翻訳であるJIS X 3010:2003を参照のこと。}}C++の国際規格ISO/IEC 14882:2003の翻訳であるJIS X 3014:2003では、「既定の実引数昇格」と呼ばれているが、同じものである。}}。例えばfloat型の実引数は、double型に拡張される。関数形式マクロva_argを使ってスタックから値を取り出す際、指定した型の値が実際には格納されていなかった場合、未定義動作を引き起こす。

上記の例では、可変長引数にdouble型の値が渡されることを想定しているが、可変長引数に間違ってintなどの整数型の値を渡してもコンパイルは通ってしまい、未定義動作を引き起こす。この例では同じdouble型の1つ以上の値を渡すことができればよいので、可変長引数を使うのではなく、単純にdoubleの配列を使って型安全に実装すべきである。

double average_of_doubles { double sum = 0; int i; assert; assert; for  { sum += values[i]; } return sum / count; }

2.2. Cプリプロセッサ・マクロ
-----------------

C言語のプリプロセッサのマクロ定義において、可変長引数マクロには、以前はトリック的な方法が使われていたが、C99で本物の可変長引数マクロが標準化された。__VA_ARGS__という識別子を使用する。

次のような関数の呼び出しに展開されるマクロを定義したいとする。関数が可変長引数をとるため、機能を制限したくなければ、マクロも可変長引数をとるようにしたい。 void realdbgprintf;

C++での可変長引数マクロの設計に問題があるためdate=2019年1月}}、以下に示すような定義は行えない。

define dbgprintf realdbgprintf<WtXmlEndTag /> この書き方だと、dbgprintfと記述した際にrealdbgprintfと展開される。関数の引数リストをコンマで終えると構文エラーを起こすため、printfのような使い勝手は得られない。

gccの独自構文では##__VA_ARGS__という識別子もサポートしている。これを利用することにより引数がゼロ個のものも定義できる。

define dbgprintf realdbgprintf<WtXmlEndTag />

gcc以外でも使用するためには__VA_ARGS__を使うほかないが、その場合は書式文字列も含めて可変長引数として渡す必要がある。

define dbgprintf realdbgprintf<WtXmlEndTag />

Microsoft Visual C++の独自拡張モードでは、ゼロ個の引数を受け取る__VA_ARGS__は末尾コンマを抑制する動作をする。

C++20では、空の可変引数を処理するための__VA_OPT__マクロをサポートする。

2.3. C++
--------

可変長引数テンプレートがC++11でサポートされた。

template class tuple;

このtupleクラステンプレートは、テンプレート引数としていくらでも型名を取れる:

tuple<std::vector, std::map<std::string, std::vector>> someTuple; // tuple インスタンス。 tuple<> emptyTuple;

std::tupleとしても標準化されている。

また、C++11ではC99との互換性向上のため、可変長引数マクロが標準化された 。

2.4. Java
---------

平均値を求める静的メソッド average の例を考える。平均値の計算には最小でも1個の値が必要であるから、固定個数の仮引数として x0 を指定しており、それ以降を任意個数の引数としている。任意個数の値は仮引数 xs に double 型の配列として格納される。 static double average { double sum = x0; int count = 1; for  { sum += x; ++count; } return sum / count; }

上記を呼び出すコード片の例は次の通りである。

System.out.println);

2.5. Scheme
-----------

平均値を求める手続き average の例を考える。平均値の計算には最小でも1個の値が必要であるから、固定個数の仮引数として x0 を指定しており、それ以降を任意個数の引数としている。任意個数の値は仮引数 xs にリストとして格納される。   )  ) )) xs) ))

上記を呼び出すコードの例は次の通りである。

) 

3. 英語
-----

enは「可変長引数の」「可変個引数の」という意味の形容詞である。C言語などでは可変長引数のことを「en」と呼ぶ。辞典などでは直訳して「変数引数」とされていることがあるが、これは誤訳である。なお、「変数引数」という言葉は、Pascalなどで変数渡しに使用される「en」の定訳であり、これは可変長引数とは違うものを指す。したがって、可変長引数のことを「変数引数」と呼ぶのは混乱の原因となる。

4. 脚注
-----

4.1. 注釈
-------

4.2. 出典
-------

5. 関連項目
-------

呼出規約
printf
scanf
書式文字列攻撃