ランニングにはちょうどよい季節だ。
さて、可変長引数について調べたのでまとめようと思う。
可変長引数とは?
- C言語の標準ライブラリ関数である printf や scanf のように任意の個数をとることができる引数のことを、可変長引数、もしくは動的引数という[1]。これらは決して不思議な力でライブラリのみに使用の許された魔法の機能なんかではなく、ユーザーも可変長引数を持つ関数を定義して使うことができる。
- 宣言は、たとえば以下のようにやる
... が可変長の引数が入るということを示す。- void function(int argc, ...);
引数の並び順として、最初に数の固定された引数が入り、後に数の可変な引数が入る。
数の固定された引数は二個でも三個でも良いが、必ず一つは用意しなければならない。
- 可変長引数の利用では、以下の専用のマクロを用いることになっている。
これらのマクロは stdarg.h に定義されており、ISO C99に準拠した実装となっている。vargs.h にも定義がなされているが、そりたは ANSI C89 で書かれたコードの互換性のためにあり、現在では非推奨となっている[2]。- va_start
- va_copy
- va_arg
- va_end
さて、MSDNの解説によると、/clr オプションを付けてコンパイルした場合、native と CLR の違いのために予期しない結果を生む場合があるとのこと。
CLRとはなんぞや?と思い調べてみたが、どうやら .Net Framework の土台となっているモノらしい。
.NET Frameword についても調べないといけないようなので、これについてはまた別の機会に調べたいと思う。
開発・実行環境
プロセッサ: Intel(R) Core(TM) i5-5200U CPU @2.20GHz
メモリ: 8.00 GB
OS: Windows 8.1 (64bit)
compiler: Microsoft (R) C/C++ Optimizing Compiler Version 19.00.24215.1 for x86
コード
デモ関数
- void print_va_collect(int arg0, ...) {
- va_list arg_ptr;
- va_start(arg_ptr, arg0);
- printf("va 0:%d\n", va_arg(arg_ptr, int));
- printf("va 1:%f\n", va_arg(arg_ptr, double));
- printf("va 2:%f\n", va_arg(arg_ptr, double));
- printf("va 3:%c\n", va_arg(arg_ptr, char));
- printf("arg5:%s\n", va_arg(arg_ptr, const char*));
- va_end(arg_ptr);
- }
試しに書いてみた、可変長引数を使う関数である。
va_list型は引数リストのポインタであるらしい。
va_start では arg_ptr に可変長引数の一個前の引数のポインタを渡す。
(ポインタをセットするマクロであることは分かったが、 &変数名 ではなく 変数名 としているのは生理的に気持ちが悪い。規格がそうなっているのだから文句を言っても仕方がないのだが…)
呼び出す側
- print_va_collect(0, 128, 0.1F, 0.2L, 'a', "abc");
固定の第一引数は、今回は特に意味を持たせていないので適当に 0 を入れておく。
あとは、128 (int)、0.1F (float)、0.2L (double)、'a' (char)、"abc" (const char*) を可変引数に入れる。
注意点は、可変長引数に float 型を入れる際は double に変換することである。
以下のようなことはできない(やってみたけど、壊れた動き方をした)。
- va_arg(arg_ptr, float)
余談だが、標準変換では、int 型よりも小さい short 型などは int 型に変換され、float 型は double 型に暗黙に変換される[3]。
scanf 関数の書式指定子が float 型には %f、double 型には %lf を用いていることと混同してはならない。
printf 関数に float 型と double 型を入れたとして、その書式指定子はともに %f なのである。
つい最近まで、この違いが分かっていなかった。
初心者にありがちな落とし穴かもしれない。
閑話休題。
感想
例えば、メッセージボックスやエディットコントロールに表示する場合など。
この可変長変数という機能を用いることで、いちいちその場で sprintf でバッファに文字列を格納するめんどくさい処理を関数化することができる。
0 件のコメント:
コメントを投稿
コメント表示は承認制に設定しています