Cラーニング-可変パラメータリスト


一、引用
自分がよく使うprintfライブラリ関数を思い出すと、いくつかの手がかりが見つかるかもしれません.
#include <stdio.h>

int
main(int argc, char const *argv[])
{
        printf("hello c
");          printf("%s
""hello world
""%d
", argv[0], argc); return 0; }

printfライブラリ関数が受信するパラメータは、複数であってもよく、ここでの複数は、1つのパラメータのみ、または2つ以上であってもよい.しかし、少なくとも1つのパラメータがあります.ライブラリ関数printfの宣言を調べてみましょう.
int printf (__const char *__restrict __format, ...);

宣言からprintfの最初のパラメータは固定(フォーマットされた文字列定数)であり、後のパラメータリストは与えられていないことがわかります.printfライブラリ関数の実装はC言語の特性を用い,可変パラメータリストをサポートする.まずprintfライブラリ関数ファミリーとscanfライブラリ関数ファミリーはこの特性を用いており,この特性の存在が有意義であることを示している.
二、どのように使うか
次に、可変パラメータリストの使用方法について説明します.
printfライブラリ関数の宣言を使用すると、可変パラメータリストをどのように定義するかがわかります.まず決定されたパラメータは前に列挙し,その後欠落しないパラメータは後に「...」とする.で行ないます.
では、後の可変パラメータリストをどのように使用しますか?ここではマクロをいくつか使います.これらのマクロはstdargに存在する.hこのヘッダファイルには、タイプvaが宣言されています.Listと3つのマクロva_start, va_arg, va_end.後続の可変パラメータリストにアクセスして使用するには、これらを使用します.まず簡単な例を示してから、これらのマクロがどのように使用されているかを説明します.この簡単な例はあまり実用的な意味がないようで、説明にしか使えません.
この例では、ある月の給料の平均値を計算することを実現します.ここでは、数ヶ月の給料が不足していないし、毎月の給料数も確定していません.結果を返すのは平均値の整数部分だけです.実装コードは次のとおりです.
#include <stdio.h>
#include <stdarg.h>

int
main(int argc, char const *argv[])
{
	int average_salary(int count_months, ...);

	int first_result = average_salary(3, 9000, 9200, 9700);
	int second_result = average_salary(1, 9700);

	printf("Three-month average wage is : %d.
", first_result); printf("One-month salary is : %d.
", second_result); return 0; } int average_salary(int count_months, ...) { va_list my_args; int count= count_months; int sum_salary = 0; va_start(my_args, count_months); while(count-- > 0) sum_salary += va_arg(my_args, int ); va_end(my_args); return sum_salary / count_months; }

コードの実装について説明します.まず、va_を宣言します.リストタイプの変数my_args.後にマクロvaを呼び出すstartは初期化作業を行い、マクロva_startの1番目のパラメータはva_ですList変数の名前、2番目のパラメータは省略記号「...」前の最後に示すパラメータ名があります.ここでもう一度説明します.もし私のaverageがsalaryのパラメータは(int count_months,int total,...)、ではマクロvaを呼び出しますstartの場合、2番目のパラメータはtotalである必要があります.va_argマクロは可変パラメータリストにアクセスするために使用され、1番目のパラメータはva_List変数の名前で、2番目のパラメータは可変パラメータリストの次のパラメータのタイプです.呼び出しva_argマクロの後にこのパラメータの値が返され、my_argsは次の可変パラメータを指す.最後に、可変パラメータリストへのアクセスが終了した後にマクロvaを呼び出す.endは可変パラメータリストへのアクセスを終了し、このマクロはva_のみを受信する.listタイプの変数名パラメータ.
三、注意事項
使用上の注意点:
  • 可変パラメータリストのパラメータは、最初から最後まで順次アクセスする必要があります.ただしパラメータへのアクセスは途中での退出は許可されていますが、ジャンプアクセスは許可されていません.すなわち、連続的なアクセスではない(一部のパラメータをスキップする制御を加えることができることに注意)、言い換えれば、この可変パラメータリストのアクセスは、一方向チェーンテーブルのアクセスと少し類似しており、毎回最初から開始され、配列のようにランダムにアクセスできないリンク指向が必要である.
  • 可変パラメータリストとしてのすべてのパラメータは、関数値を渡すとデフォルトのパラメータタイプの昇格を実行します.
  • はstdargにあります.hヘッダファイル定義のこれらのマクロは,実際に存在するパラメータの数を判断することはできず,各パラメータのタイプも判断できない.(ただし、printf関数のような名前付きパラメータで実現することができ、printf関数の実装は後述する).
  • 四、printf関数の実現
    ここではFreeBSDシステムにおける実装(参考資料2参照)を貼り付け,一部の呼び出しのみを与え,興味があれば自分で調べることができる.
    int
    printf(char const * __restrict fmt, ...)
    {
    	int ret;
    	va_list ap;
    
    	va_start(ap, fmt);
    	ret = vfprintf(stdout, fmt, ap);
    	va_end(ap);
    	return (ret);
    }
    int
    vfprintf(FILE * __restrict fp, const char * __restrict fmt0, va_list ap)
    {
    	return vfprintf_l(fp, __get_locale(), fmt0, ap);
    }
    int
    vfprintf_l(FILE * __restrict fp, locale_t locale, const char * __restrict fmt0,
    		va_list ap)
    {
    	int ret;
    	FIX_LOCALE(locale);
    
    	FLOCKFILE(fp);
    	/* optimise fprintf(stderr) (and other unbuffered Unix files) */
    	if ((fp->_flags & (__SNBF|__SWR|__SRW)) == (__SNBF|__SWR) &&
    	    fp->_file >= 0)
    		ret = __sbprintf(fp, locale, fmt0, ap);
    	else
    		ret = __vfprintf(fp, locale, fmt0, ap);
    	FUNLOCKFILE(fp);
    	return (ret);
    }
    static int
    __sbprintf(FILE *fp, locale_t locale, const char *fmt, va_list ap)
    {
    	int ret;
    	FILE fake = FAKE_FILE;
    	unsigned char buf[BUFSIZ];
    
    	/* XXX This is probably not needed. */
    	if (prepwrite(fp) != 0)
    		return (EOF);
    
    	/* copy the important variables */
    	fake._flags = fp->_flags & ~__SNBF;
    	fake._file = fp->_file;
    	fake._cookie = fp->_cookie;
    	fake._write = fp->_write;
    	fake._orientation = fp->_orientation;
    	fake._mbstate = fp->_mbstate;
    
    	/* set up the buffer */
    	fake._bf._base = fake._p = buf;
    	fake._bf._size = fake._w = sizeof(buf);
    	fake._lbfsize = 0;	/* not actually used, but Just In Case */
    
    	/* do the work, then copy any error status */
    	ret = __vfprintf(&fake, locale, fmt, ap);
    	if (ret >= 0 && __fflush(&fake))
    		ret = EOF;
    	if (fake._flags & __SERR)
    		fp->_flags |= __SERR;
    	return (ret);
    }

    __vfprintf関数が長すぎるので、自分で調べてください.
    五、参考資料
    1. Pointers on c, Kenneth A. Reek
    2. http://www.freebsd.org/cgi/cvsweb.cgi/src/lib/libc/stdio/#dirlist
    説明:
    間違いがあれば、皆さんに指摘してもらい、一緒に討論して指導してください.
    上記の例のプログラムの完全なコードのダウンロードリンク:
    https://github.com/zeliliu/BlogPrograms/tree/master/C%20C%2B%2B%E5%AD%A6%E4%B9%A0/variable%20parameters
    最終更新日時:2013-06-13