在寫乙個函式時,經常會有實現乙個引數未知或不是常數的函式的需求。printf
就是這樣的乙個函式,在 section 9.11中有詳細描述。接下來的例子將想你展示如何宣告乙個這樣的函式。
int f(int, ... )
int g()
//example 9.5
為了得到被呼叫的函式的引數,
標頭檔案中函式的宣告必須要被包括。這就引入了乙個新型別va_list
和三個對這個型別的物件的函式,它們是va_start
,va_arg
, 和va_end
。
在獲取乙個變數引數列表之前,va_start
一定要被呼叫,它被定義成
#include
void va_start(va_list ap, parmn);
va_start
巨集初始了ap
為 接下來的函式va_arg
和va_end
所用。va_start
的第二個引數parmn
是作為識別符號命名了函式定義(在…前面那個)中變數參數列中最右邊引數。識別符號parmn
不能被宣告成register
的儲存類或作為乙個函式或陣列型別。
一旦初始化了,提供的這些引數就可以在接下來被va_arg
巨集所使用。這很奇怪,因為返回的型別是被巨集的乙個引數所定義的。注意這不可能由乙個真正的函式去實現,而只能是乙個巨集。它被定義成
#include
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
void va_end(va list ap);
接下來的例子展示了va_start
,va_arg
, 和va_end
在實現乙個返回最大整型引數的函式裡的用法
#include
#include
int maxof(int, ...) ;
void f(void);
main()
int maxof(int n_args, ...)
va_end(ap);
return max;
}void f(void)
//example 9.6
閱讀原文 C C 可變引數
函式 使用va list巨集組解決變參問題 1 首先定義va list型的變數,這個變數是指向引數的指標。2 然後用va start巨集初始化剛定義的va list變數,這個巨集的第二個引數是 第乙個可變引數的前乙個引數 3 再用va arg得到可變引數,第二個引數是 可變引數 的型別。4 最後用v...
C C 可變引數
函式 使用va list巨集組解決變參問題 1 首先定義va list型的變數,這個變數是指向引數的指標。2 然後用va start巨集初始化剛定義的va list變數,這個巨集的第二個引數是 第乙個可變引數的前乙個引數 3 再用va arg得到可變引數,第二個引數是 可變引數 的型別。4 最後用v...
C C 可變引數
為了解決這些問題,我們首先要解釋cdecl呼叫約定 參見論調用約定 所有使用不定引數的函式必須是使用cdecl 全域性函式 或者this call 類成員函式 呼叫約定。該約定對於引數傳遞規定如下 引數從右向左入棧 也就是如果你呼叫f a,b,c 則c先入棧,然後是b,最後是a入棧 呼叫者負責清理堆...