C において printf() のような可変引数関数に関しての記事です。printf() や scanf() のような関数は、その引数にいくつでも変数を渡すことができます。もちろん、そのような関数をプログラマー自身も作成できるので、その方法について見ていきます。
引数の数を引数で指定する場合
まずは、可変引数関数が引数の数を引数として受け取る例です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | #include <stdio.h> #include <stdarg.h> int main( void ) { double sumargs(int n, ...); printf("Sum of three %f\n", sumargs(3, 10.0, 20.0, 30.0)); printf("Sum of two %f\n", sumargs(2, 10.0, 20.0)); printf("Sum of zero %f\n", sumargs(0)); return 0; } double sumargs(int n, ...) { va_list argp; float sum = 0; va_start(argp, n); while (n--) { sum += va_arg(argp, double); } va_end(argp); /* required */ return sum; } /* Output: Sum of three 60.000000 Sum of two 30.000000 Sum of zero 0.000000 Program ended with exit code: 0 */ |
sumargs() が可変引数関数です。最初の引数 n で、いくつの引数を受け取るのか指定しています。関数のプロトタイプでは、… が可変の引数を受け取るということを宣言しています。この名前のない引数(…)は、最後の名前のある引数(n)の後に宣言しなければなりません。つまり、少なくともひとつは名前のある引数がなければいけません。va_start()、va_arg()、va_end() の関数は、stdarg.h にあります。
var_start() は、va_list のポインターを初期化します。その結果、va_list argp は、可変引数の一番最初の変数を指します。そして、va_arg() が呼ばれるたびに、次の引数へこのポインター移動します。そのために、ポインターが何 byte 移動するかを指定するのが va_arg() の2つ目の引数です。この可変引数関数の 1つ目の引数 n で、いくつか引数があるか指定しているので、その分だけループを回します。最後に、va_end() は、名前のない引数のスタックを解放します。これは省略することができません。
引数にセンティネルを設定する場合
次に、可変変数にセンチネル値を設定する方法があります。これは明示的にいくつの引数をしていする代わりに、可変変数の最後にこれが引数の終わりだよと印を付ける方法です。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | #include <stdio.h> #include <stdarg.h> #include <string.h> int main( void ) { void char_find(char *string, …); char_find("Hello", 'e', 'l', 'i', 'k', 'o', '#’); return 0; } void char_find(char *string, ...) { va_list argp; char c; va_start(argp, string); while ((c = va_arg(argp, int)) != '#') { printf("The character %c is%s in %s\n", c, strchr(string, c) ? "" : " not", string); } va_end(argp); return; } /* Output: The character e is in Hello The character l is in Hello The character i is not in Hello The character k is not in Hello The character o is in Hello Program ended with exit code: 0 */ |
基本は、引数の数を指定する場合と同じです。while 文の条件において、可変引数がセンティネルであるかを判定しています。