第16章 仰山の引数

 printf はいくらでも引数を取ることができます。不思議ですねー。今回はそういった関数の作り方について話そうと思います。


 では、今回の要点です。


 では、いってみましょう。


 今回はいくらでも引数の取れる関数を作ってみたいと思います。

 printf はいくらでも引数を取ることができます(printf については第1部第24章を参照して下さい)。しかし、普通の関数では引数を多く渡すとエラーになります。ということは、何か特別な構文があると思われます。

 ヘルプを見れば分かりますが、printf のプロトタイプはこのようになっています。

int printf(const char*, ...);

 引数が ... になっていますね。こうすれば、いくらでも引数を取ることができるようになります。

 しかし、これでは引数に名前がありません。引数が渡されたとしても、どのように使えばいいのでしょうか? とりあえず、プログラムを見てみましょう。

プログラム実行結果
// VarArg1.cpp
#include <stdio.h>
#include <stdarg.h>

int DispLetters(int first, ...)
{
    int     letter = first;
    va_list args;

    va_start(args, first);
    while(letter != 0)
    {
        putchar(letter);
        letter = va_arg(args, int);
    }
    va_end(args);
}

int main()
{
    DispLetters('H', 'e', 'l', 'l', 'o', ' ',
                'w', 'o', 'r', 'l', 'd', '!', '\n', 0);

    return 0;
}
Hello world!

 DispLetters という関数を作ってみました。この関数は、引数の値が0になるまで文字を表示する関数です。main では何と14個もの引数を渡しています。

 で、赤い部分が可変個引数を使うためのコードです。

 概要を説明します。先ず、letter に引数を渡します。そして、それを一文字出力する関数 putchar を使って文字を出力します。そして、次の引数を letter に渡します。これを、渡される引数の値が0になるまで続けます。

 では、順を追ってみてみましょう。

int     letter = first;
va_list args;

 変数 letter を宣言し、そして初めの引数で初期化しています。これは構いませんね。

 問題は次です。va_list という型の変数 args を宣言しています。これは可変個引数を使うために必要な変数です。この変数は、次のマクロで初期化されます。

va_start(args, first);

 初めの引数はさっきの args です。で、次に指定するのは ... の直前にある変数です。

 で、とりあえず最初の変数は普通に表示して、次は

letter = va_arg(args, int);

です。va_arg マクロで次の引数を受け取っています。マクロの初めの引数はやっぱり args です。で、次の引数は取得する引数の型です。

 このようにして引数を順次受け取っていくのです。

 で、最後は args の後始末です。

va_end(args);

 これは args を使えない状態にするマクロです。もう関数が終わるので実際的にはしなくても構いませんが、念のためしておく方がいいでしょう。


 ちょっと仕組みについて話したいと思います。

 仮引数は実はメモリ上に順番に並んでいます。なので、... の直前の変数のアドレスが分かれば、... の各引数のアドレスも分かるというわけです。

 ということで、va_list はポインタ型です。va_start で初期化し、va_arg でポインタを進め、va_end でその変数を NULL にセットするという流れなのです。

 また、1つ注意することがあるとすれば、引数は int 型のサイズの倍数に拡張されて渡されます。signed char は signed int に、unsigned char は unsigned int に拡張されます。

 まぁ、これを char なら char のまま扱うときには何も問題ないので、それほど意識する必要はないと思います。


 最後に1つ。ここでやったことは一般的なC/C++言語での話なのですが、UNIX System V では方法が変わってきます。詳しくはヘルプを見て下さい。


 では、今回の要点です。


 次回からはプリプロセッサディレクティブについての話をしたいと思います。頭に # のつくやつです。

 それでは、また。


第15章 伸縮自在 | 第17章 if...

Last update was done on 2000.8.12

この講座の著作権はロベールが保有しています