C言語におけるパラメトリック関数の解析


1.パラメトリック関数:
すなわち、パラメータの個数、タイプが不定の関数であり、printf()関数のように最も一般的である.
2.ヘッダファイル:
初期のUnix System V互換方式ヘッダファイル名は,ANSI規格指定ヘッダファイル名,GCCは現在include varargsをサポートしていない.hファイル
3.マクロ定義:
va_list(), va_arg(), va_start(), va_end();

4.ソースの例:
printk()ソースコード(削除):
asmlinkage int printk(const char *fmt, ...)
{
	va_list args;
	int r;

	va_start(args, fmt);
	r = vprintk(fmt, args);
	va_end(args);

	return r;
}
のうち、
... 不定パラメータを表します.
大まかな流れは次のとおりです.
(1). va_を宣言list変数args、va_Listタイプはchar*;
typedef char *va_list;
(2). 呼び出しva_starts(args,fmt)関数は、fmtパラメータの後ろの不定パラメータの最初のパラメータアドレスを取得する.
va_start(args, fmt);
(3). 呼び出しサブ関数vprintk()
(4). 呼び出し関数va_end(args)はargがNULLを指すようにする
5.マクロ定義解析
va_startのマクロ定義は次のとおりです.
#define va_start(ap, A)      (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))

そのうち、bndマクロは以下のように定義される.
#define _bnd(X, bnd)         (((sizeof (X)) + (bnd)) & (~(bnd)))

_bnd(X,bnd)の機能は、Xタイプが現在のマシンで位置合わせされた空間の大きさを返すことです.4バイトで位置合わせされた場合、AUPBNDの値は3であり、Xタイプの大きさが4より小さく、占有スペースの大きさが4であり、4より大きい場合は、4の整数倍の整数で戻る
関数呼び出しの場合、パラメータがスタックに入る順序は右から左に、スタック空間アドレスは下に延びるので、一番左のパラメータから一番右のパラメータはアドレスが連続的に増加するメモリです.したがって、va_startマクロの役割は、ポインタパラメータapが固定パラメータ(char*fmt)後の最初のパラメータを指すようにすることである.
最初のパラメータを指すと、マクロva_を呼び出すことができます.arg()は、次のパラメータアドレスを取得し、以下のように定義されます.
#define va_arg(ap, T)  (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))

まずapのアドレスを次のパラメータアドレスに指し、式は最初のパラメータの値を返します.
va_endマクロの定義は簡単です.
#define va_end(ap)          (void) 0
このバージョン定義でva_endは何もせず、apポインタをNULLに向ける必要があると考えられています.
#define va_end(ap)       ap=(char*)0
これによりコードから「野ポインタ」が消去される.
6.例
機能:いくつかの数の二乗和を求める関数を実現し、少なくとも1つのパラメータがあり、パラメータ終了フラグは1つの-1のパラメータである.フラグビットが存在する理由は、パラメータ関数が関数の読み込みを停止するタイミングを決定できないためである.
関数コードは次のとおりです.
int sum_square(int n,...)
{
	int k = 0;
	int temp =0;
	va_list args;

	if(n == -1){
		return 0;
	}

	va_start(args,n);
	k+= n*n;
	while((temp=va_arg(args,int)) != -1){
		k+= temp * temp;	
	}
	va_end(args);
	return k;
}

関数呼び出しに-1パラメータがない場合、通常は不正なメモリへのアクセスによるセグメントエラーのため、予想しにくい結果が発生します.
printf("sum of square is %d 
",sum_square(1,2,3,4,-1));
運転後表示:
sum of square is 30