可変長引数をとる関数をつくるには,va_*
マクロを使う.
大雑把な流れは以下のとおり.
foo(type1 x, ...) { va_list args; va_start(args, x); while (ナントカ) { type2 arg = va_arg(args, type2); } va_end(args);
具体例がないとわかりにくいと思うので,次のような関数を例に説明する.
プロトタイプ | int sum(int num, ...); |
---|---|
機能 | int 型の引数群の合計値を計算する |
引数 |
第1引数num :可変長引数の個数.第2引数以降( ... ):合計を計算したいint 型の並び.
|
返値 | 第2引数以降で指定したnum 個のint 型変数・定数の合計値 |
使用例 |
3つの整数{10,11,12}の合計を計算する場合,次のように呼び出す.sum(3, 10, 11, 12);
|
可変長引数をとる関数といっても,少なくとも最初の1個の引数(上の例ではint num
の部分)は明確になっていなければならない
[理由についてはあとの説明を読み進めるとわかります].
したがって,可変長引数をとる関数は必ず“foo(type x, ...)
のような形式になる.
[なので,可変長引数をとる関数というのは,正確には1個以上の可変個の引数をとる関数といえます.
おなじみのprintf
(もっともよく知られている可変長引数をとるC言語標準関数)も最初の1個の引数だけは任意ではなくて,
必ず書式文字列がくることになっていますよね.]
この関数を実装すると,だいたい次のようになる.
#include <stdarg.h> int sum(int num, ...) { int result = 0; int ival, i; va_list args; va_start(args, num); for (i = 0; i < num; ++i) { ival = va_arg(args, int); result += ival; } va_end(args); return result; }
マクロ | 説明 |
---|---|
va_list args; |
引数のリストを保持するための変数args を宣言する.
今後このargs を介して可変長引数部分にアクセスする.
|
va_start(args, num); |
args の初期化.
第2引数にnum を与えているのは,num が可変長引数が始まる位置の目印になるため.
つまり,可変長引数部分の直前の引数を与える必要がある.
|
va_arg(args, int); |
次の引数を型type とみなして読み込んでくる.
この例では可変長引数部分はすべてint と決め込んで読み込んでいるけど,
printf みたいに第1引数でどの型として読み込むべきかについてのヒントをもらって
いろんな型の足し算ができるようにしたらステキかも知れない.
|
va_end(args); |
後始末.[これってほったらかしにしたらいけないの?と思いたくなるけど…] |
float
はdouble
に変換されるdouble f = 3.15; printf("%f\n", f);
このプログラムは普通に見るものだけど,よく考えてみるとちょっとしたミステリーが潜んでいる.
第1引数の書式文字列“%f
”をみて,第2引数を浮動小数点数として処理しようとするのだけど,
printf
関数はf
がfloat
型とdouble
型のどちらなのかはどうやって判別してるの?
float
とdouble
は最初の1ビットが符号部であることをのぞいてバイト数も書式もちがってるから,
あらかじめどちらの型なのかわかっていないと正しい表示ができなくなってしまう.
実は可変長引数では,必ずfloat
型はdouble
型に変換されてから関数にわたされるという規則になっている.
したがって,可変長引数を受けとる側の関数は,必ずdouble
がわたってくると仮定して処理すればよい.
他にもchar
型はint
型に変換されてからわたされるというような規則がある.
詳細については以下の資料を参照.
printf()
deal with %f
?”に可変長引数における型変換規則の解説がある.
float
→double
,char
→int
)について追記