Menu

Category

Archive

logo


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

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

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 文の条件において、可変引数がセンティネルであるかを判定しています。