在寫一個函數時,經常會有實現一個參數未知或不是常數的函數的需求。printf
就是這樣的一個函數,在 Section 9.11中有詳細描述。接下來的例子將想你展示如何聲明一個這樣的函數。
int f(int, ... ) {
.
.
.
}
int g() {
f(1,2,3);
}
//Example 9.5
爲了得到被調用的函數的參數, <stdarg.h>
頭文件中函數的聲明必須要被包括。這就引入了一個新類型va_list
和三個對這個類型的對象的函數,它們是va_start
, va_arg
, 和va_end
。
在獲取一個變量參數列表之前,va_start
一定要被調用,它被定義成
#include <stdarg.h>
void va_start(va_list ap, parmN);
va_start
宏初始了ap
爲 接下來的函數va_arg
和va_end
所用。va_start
的第二個參數parmN
是作爲標識符命名了函數定義(在…前面那個)中變量參數表中最右邊參數。標識符parmN
不能被聲明成register
的存儲類或作爲一個函數或數組類型。
一旦初始化了,提供的這些參數就可以在接下來被va_arg
宏所使用。這很奇怪,因爲返回的類型是被宏的一個參數所定義的。注意這不可能由一個真正的函數去實現,而只能是一個宏。它被定義成
#include <stdarg.h>
type va_arg(va_list ap, type);
每次對這個宏的調用都會提取參數列表中的下一個參數作爲一個特定類型的值。va_list
參數必須是一個被va_start
初始化的參數。如果接下來的參數不是特定的那種類型,那麼這成了一個未定義的行爲。在這裏要注意避免可能由於數值類型轉換而導致的問題。用char
或者short
作爲va_arg
的第二個參數總是錯誤的:這些類型總是會提升到signed int
或unsigned int
中的一個,以及float
會轉換成double
。注意是執行時才定義是否對象聲明爲類型char
, unsigned char
, unsigned short
,無符號的那些位閾會提升至unsigned int
,還是複雜化va_arg
。這可能是一些預期以外的微妙的事情發生的地方;時間會說明一切。
當va_arg
被調用卻沒有更多的參數時,這種行爲也是不被定義的。
這裏的type
(類型)參數的類型名,必須可以簡單地用添加一個*
來轉換成一個指向一個對象的指針 。簡單類型如char
就可以(因爲char *
就是一個指向字符對象的指針),但是字符數組就不行(char []
不能用添加*
的方式轉換成‘指向字符數組的指針’)。好在,數組還是很好處理的–只要記住數組名作爲函數調用實參的時候本身就會轉換成一個指針就可以了。對於一個’字符數組’參數類型而言,正確的type
將會是char *
。
當所有參數都被處理了的時候,va_end
函數應該被調用,這將會防止va_list
裏的那些已經被用過的參數再次呈遞上去。如果va_end
沒有被使用,這種行爲是未定義的。
在調用va_end
以後,整個參數列表可以用再調用va_start
的方法被重新遍歷。va_end
函數被聲明爲
#include <stdarg.h>
void va_end(va list ap);
接下來的例子展示了va_start
,va_arg
, 和va_end
在實現一個返回最大整型參數的函數裏的用法
#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