爲了解決這些問題,我們首先要解釋cdecl調用約定(參見論調用約定),所有使用不定參數的函數必須是使用cdecl(全局函數)或者this call(類成員函數)調用約定。該約定對於參數傳遞規定如下:
- 參數從右向左入棧(也就是如果你調用f(a,b,c),則c先入棧,然後是b,最後是a入棧)
- 調用者負責清理堆棧
在設計具有不定參數列表的函數的時候,我們有兩種方法來確定到底多少參數會被傳遞進來。
方法1是在類型固定的參數中指明後面有多少個參數以及他們的類型。printf就是採用的這種方法,它的format參數指明後面每個參數的類型。
方法2是指定一個結束參數。這種情況一般是不定參數擁有同樣的類型,我們可以指定一個特定的值來表示參數列表結束。
- #include "stdarg.h"
- using namespace std;
- int sum(int count, ...)
- {
- int sum_value=0;
- va_list args;
- va_start(args,count);
- while(count--)
- {
- sum_value+=va_arg(args,int);
- }
- va_end(args);
- return sum_value;
- }
- int _tmain(int argc, _TCHAR* argv[])
- {
- cout<<sum(5,1,2,3,4,5);//輸出15
- }
在vc6,va_start函數定義爲:
#define va_start(ap,v) ( ap = (va_list)&v + _INTSIZEOF(v) )
其中_INTSIZEOF(n)計算比n大的sizeof(int)的最小倍數,如果n=101,則_INTSIZEOF(n)爲104。
va_start執行完畢後,ap指向變量v後第一個4字節對齊的地址。例如,v的地址爲0x123456, v的大小爲13,則v後面的下一個與字邊界對齊的地址爲0x123456+0x0D=0x123463再調整爲與4字節對齊的下一個地址,也就是 0x123464.
va_arg函數定義爲:
分析與va_start一樣,它的結果是使ap指向當前變量的下一個變量。
這樣,我們只要在開始時使用va_start把不定參數列表賦值給ap,然後依次用va_arg獲得不同參數即可。