Menu

Category

Archive

logo


printf() のような可変引数関数の書き方

2014-06-02 10:45:00 +0900
  • このエントリーをはてなブックマークに追加

C において printf() のような可変引数関数に関しての記事です。printf() や scanf() のような関数は、その引数にいくつでも変数を渡すことができます。もちろん、そのような関数をプログラマー自身も作成できるので、その方法について見ていきます。

引数の数を引数で指定する場合

まずは、可変引数関数が引数の数を引数として受け取る例です。

 1 #include <stdio.h>
 2 #include <stdarg.h>
 3 
 4 int main( void )
 5 {
 6     double sumargs(int n, ...);
 7 
 8     printf("Sum of three %f\n", sumargs(3, 10.0, 20.0, 30.0));
 9     printf("Sum of two   %f\n", sumargs(2, 10.0, 20.0));
10     printf("Sum of zero  %f\n", sumargs(0));
11 
12     return 0;
13 }
14 
15 double sumargs(int n, ...)
16 {
17     va_list argp;
18     float sum = 0;
19 
20     va_start(argp, n);
21 
22     while (n--)
23     {
24         sum += va_arg(argp, double);
25     }
26 
27     va_end(argp); /* required */
28 
29     return sum;
30 }
31 
32 /* Output:
33     Sum of three 60.000000
34     Sum of two   30.000000
35     Sum of zero  0.000000
36     Program ended with exit code: 0
37 */

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 #include <stdio.h>
 2 #include <stdarg.h>
 3 #include <string.h>
 4 
 5 int main( void )
 6 {
 7     void char_find(char *string, );
 8 
 9     char_find("Hello", 'e', 'l', 'i', 'k', 'o', '#’);
10 
11     return 0;
12 }
13 
14 void char_find(char *string, ...)
15 {
16     va_list argp;
17     char c;
18 
19     va_start(argp, string);
20 
21     while ((c = va_arg(argp, int)) != '#')
22     {
23         printf("The character %c is%s in %s\n", c, strchr(string, c) ? "" : " not", string);
24     }
25 
26     va_end(argp);
27 
28     return;
29 }
30 
31 /* Output:
32     The character e is in Hello
33     The character l is in Hello
34     The character i is not in Hello
35     The character k is not in Hello
36     The character o is in Hello
37     Program ended with exit code: 0
38 */

基本は、引数の数を指定する場合と同じです。while 文の条件において、可変引数がセンティネルであるかを判定しています。