Last Modified :Wednesday, 16-Feb-2005 23:22:13 JST
トップページへ
乱丁, 落丁, 間違い等のご指摘は
kurihara.toru [at]kochi-tech.ac.jpまでお願いいたします.
(初版:22/Apr/2004)
(第2版:28/Apr/2004)参考文献を追加
(第3板:16/Feb/2005)SaveCSV2を追加
可変引数リストを用いたプログラミング
はじめに
可変引数リストとは,
引数の個数があらかじめ決まっていない場合における
名前なし引数のリストのことである.
例えば, 実験時のデータの保存をするときに,
あれも保存したい、これも保存したいと思うと
そのたびに関数の種類を増やすのはあまりにも馬鹿げている.
cからc++になって, 同じ関数名でも引数の型によって呼び出される実体が
変わったため多少は楽になったが,
関数をたくさん用意するのは馬鹿げているだろう.
そこで,可変引数リストなるものを使うことにする.
例えば, printfなどは引数の数が決まっていないため,
これを用いてプログラムされている.
ヘッダ
#include
というのが必要なヘッダである.
例
では, 具体的にfloat型の引数をいくつか受け取り,
ファイルにcsv形式で書き出すプログラムを見てみよう.
引数arg_numは引数として受け取るfloat型配列の個数,
sizeは配列のサイズである.
#include
void SaveCSV(FILE *fp,int arg_num,int size,...)
{
int i,k;
float **pfval;
va_list ap; //可変長リストの宣言
pfval = new float* [arg_num];
va_start(ap,size); //可変長リストの初期化,2つめの変数は最後の名前あり引数
for(k=0;k<arg_num;k++){
pfval[k]=(float*)va_arg(ap,double*);//可変長リストから値を読み出す
}
for(i=0;i<size;i++){
for(k=0;k<arg_num;k++){
fprintf(fp,"%f,",*(pfval[k]+i));
}
fprintf(fp,"\n");
}
va_end(ap);//可変長リストの終了
delete [] pfval;
}
引数の"..."というのが, 数が決まっていない引数をあらわす部分である.
可変引数リストは,最低1個の名前付引き数と"..."からなり,
まず,va_list型として可変長リストapを宣言する.
可変個の引数リストにアクセスを開始するにはva_start()を使用する.
必ずこれを最初に行う必要があるのだ.
void va_start(va_list ap , last);
ここで2個目の引数lastは, 名前のついている固定引数の最後の変数名をいれよう.
この時点で, 可変引数リストapには, 次の引数のポインタが格納されている.
次に, 可変引数リストから値を得るにはva_argを用いる.
TYPE va_arg(va_list ap , TYPE);
typeは, 引数リストから受け取る値の型を表している.
この型に応じたバイト数だけ左辺の代入される.
この例では, float型へのポインタを格納したいのだが,
va_argはdouble型へのポインタしか書けないために,
キャストを行っている.(g++ではfloat*も通るようです.)
最後に,引数リストを使い終わったら,
va_endをしなくてはならない.
void va_end(va_list ap);
例2
上のSaveCSVでは,
引数の配列はすべてfloat型の縛りがありました.
次の例は, 任意の型(下の例ではint, float, double)でできるようにしました.
ただし, 美しくありません. どなたかより良い方法がありましたら,
教えてください.
第2引数に書式を指定することで実現しています.
たとえば, "dfg"など. ただし, printfのように%つけません.
また, printfではfloatもdoubleも%fでしたので,
ここでは, f:float, g:doubleということにしています.
void SaveCSV2(FILE *fp,char *fmt, int size,...)
{
int i, k, num;
void **pval; //引数リストの指すポインタへのポインタ
va_list ap; //可変長リストの宣言
num=strlen(fmt);
pval = new void* [num];
va_start(ap, size); //可変長リストの初期化
for(i=0;i<num;i++) pval[i]=va_arg(ap,int*); //ポインタなんだから一緒さ
for(i=0;i<size;i++){
for(k=0;k<num;k++){
switch(fmt[k]){ //fmt[k]により, int, float, doubleを振り分ける
case 'd': //int型
fprintf(fp, "%d,", *(((int*)pval[k])++));//型により進めるアドレスが違うので正しい型にキャストする
break;
case 'f': //float型
fprintf(fp, "%f,",*(((float*)pval[k])++));
break;
case 'g': //double型
fprintf(fp, "%f,",*(((double*)pval[k])++));
break;
}
}
fprintf(fp,"\n");
}
va_end(ap);
delete [] pval;
}
参考文献
http://black.sakura.ne.jp/~third/programming/c/c62.html
http://www.linux.or.jp/JM/html/LDP_man-pages/man3/stdarg.3.html
http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/vclib/html/_crt_va_arg.2c_.va_end.2c_.va_start.asp