在寫(xiě)一個(gè)函數(shù)時(shí)患朱,經(jīng)常會(huì)有實(shí)現(xiàn)一個(gè)參數(shù)未知或不是常數(shù)的函數(shù)的需求裁厅。printf
就是這樣的一個(gè)函數(shù),在 Section 9.11中有詳細(xì)描述侨艾。接下來(lái)的例子將想你展示如何聲明一個(gè)這樣的函數(shù)。
int f(int, ... ) {
.
.
.
}
int g() {
f(1,2,3);
}
//Example 9.5
為了得到被調(diào)用的函數(shù)的參數(shù)袋励, <stdarg.h>
頭文件中函數(shù)的聲明必須要被包括插龄。這就引入了一個(gè)新類型va_list
和三個(gè)對(duì)這個(gè)類型的對(duì)象的函數(shù)科展,它們是va_start
, va_arg
, 和va_end
才睹。
在獲取一個(gè)變量參數(shù)列表之前甘邀,va_start
一定要被調(diào)用,它被定義成
#include <stdarg.h>
void va_start(va_list ap, parmN);
va_start
宏初始了ap
為 接下來(lái)的函數(shù)va_arg
和va_end
所用坞琴。va_start
的第二個(gè)參數(shù)parmN
是作為標(biāo)識(shí)符命名了函數(shù)定義(在...前面那個(gè))中變量參數(shù)表中最右邊參數(shù)逗抑。標(biāo)識(shí)符parmN
不能被聲明成register
的存儲(chǔ)類或作為一個(gè)函數(shù)或數(shù)組類型寒亥。
一旦初始化了溉奕,提供的這些參數(shù)就可以在接下來(lái)被va_arg
宏所使用忍啤。這很奇怪,因?yàn)榉祷氐念愋褪潜缓甑囊粋€(gè)參數(shù)所定義的鳄梅。注意這不可能由一個(gè)真正的函數(shù)去實(shí)現(xiàn)未檩,而只能是一個(gè)宏。它被定義成
#include <stdarg.h>
type va_arg(va_list ap, type);
每次對(duì)這個(gè)宏的調(diào)用都會(huì)提取參數(shù)列表中的下一個(gè)參數(shù)作為一個(gè)特定類型的值校赤。va_list
參數(shù)必須是一個(gè)被va_start
初始化的參數(shù)筒溃。如果接下來(lái)的參數(shù)不是特定的那種類型,那么這成了一個(gè)未定義的行為浑测。在這里要注意避免可能由于數(shù)值類型轉(zhuǎn)換而導(dǎo)致的問(wèn)題歪玲。用char
或者short
作為va_arg
的第二個(gè)參數(shù)總是錯(cuò)誤的:這些類型總是會(huì)提升到signed int
或unsigned int
中的一個(gè),以及float
會(huì)轉(zhuǎn)換成double
岖圈。注意是執(zhí)行時(shí)才定義是否對(duì)象聲明為類型char
, unsigned char
, unsigned short
钙皮,無(wú)符號(hào)的那些位閾會(huì)提升至unsigned int
,還是復(fù)雜化va_arg
导匣。這可能是一些預(yù)期以外的微妙的事情發(fā)生的地方茸时;時(shí)間會(huì)說(shuō)明一切。
當(dāng)va_arg
被調(diào)用卻沒(méi)有更多的參數(shù)時(shí)缓待,這種行為也是不被定義的。
這里的type
(類型)參數(shù)的類型名田晚,必須可以簡(jiǎn)單地用添加一個(gè)*
來(lái)轉(zhuǎn)換成一個(gè)指向一個(gè)對(duì)象的指針 国葬。簡(jiǎn)單類型如char
就可以(因?yàn)?code>char *就是一個(gè)指向字符對(duì)象的指針),但是字符數(shù)組就不行(char []
不能用添加*
的方式轉(zhuǎn)換成‘指向字符數(shù)組的指針’)接奈。好在通孽,數(shù)組還是很好處理的--只要記住數(shù)組名作為函數(shù)調(diào)用實(shí)參的時(shí)候本身就會(huì)轉(zhuǎn)換成一個(gè)指針就可以了。對(duì)于一個(gè)'字符數(shù)組'參數(shù)類型而言互捌,正確的type
將會(huì)是char *
。
當(dāng)所有參數(shù)都被處理了的時(shí)候行剂,va_end
函數(shù)應(yīng)該被調(diào)用,這將會(huì)防止va_list
里的那些已經(jīng)被用過(guò)的參數(shù)再次呈遞上去腌巾。如果va_end
沒(méi)有被使用铲觉,這種行為是未定義的。
在調(diào)用va_end
以后灯荧,整個(gè)參數(shù)列表可以用再調(diào)用va_start
的方法被重新遍歷盐杂。va_end
函數(shù)被聲明為
#include <stdarg.h>
void va_end(va list ap);
接下來(lái)的例子展示了va_start
,va_arg
, 和va_end
在實(shí)現(xiàn)一個(gè)返回最大整型參數(shù)的函數(shù)里的用法
#include <stdarg.h>
#include <stdio.h>
int maxof(int, ...) ;
void f(void);
main(){
f();
exit(EXIT_SUCCESS);
}
int maxof(int n_args, ...){
register int i;
int max, a;
va_list ap;
va_start(ap, n_args);
max = va_arg(ap, int);
for(i = 2; i <= n_args; i++) {
if((a = va_arg(ap, int)) > max)
max = a;
}
va_end(ap);
return max;
}
void f(void) {
int i = 5;
int j[256];
j[42] = 24;
printf("%d\n",maxof(3, i, j[42], 0));
}
//Example 9.6