va_start va_end 的使用和原理
1:當無法列出傳遞函式的所有實參的型別和數目時,可用省略號指定參數列
void foo(...);
void foo(parm_list,...);
2:函式引數的傳遞原理
函式引數是以資料結構:棧的形式訪問,從右至左入棧.eg:
先介紹一下可變參數列的呼叫形式以及原理:
void func(int x, float y, char z);
那麼,呼叫函式的時候,實參 char z 先進棧,然後是 float y,最後是 int x,因此在記憶體中變數的存放次序是 x->y->z,因此,從理論上說,我們只要探測到任意乙個變數的位址,並且知道其他變數的型別,通過指標移位運算,則總可以順藤摸瓜找到其他的輸入變數。
下面是 裡面重要的幾個巨集定義如下:
typedef char* va_list;
void va_start ( va_list ap, prev_param ); /* ansi version */
type va_arg ( va_list ap, type );
void va_end ( va_list ap );
va_list 是乙個字元指標,可以理解為指向當前引數的乙個指標,取參必須通過這個指標進行。
在呼叫參數列之前,定義乙個 va_list 型別的變數,(假設va_list 型別變數被定義為ap);
然後應該對ap 進行初始化,讓它指向可變參數列裡面的第乙個引數,這是通過 va_start 來實現的,第乙個引數是 ap 本身,第二個引數是在變參表前面緊挨著的乙個變數,即「...」之前的那個引數;
然後是獲取引數,呼叫va_arg,它的第乙個引數是ap,第二個引數是要獲取的引數的指定型別,然後返回這個指定型別的值,並且把 ap 的位置指向變參表的下乙個變數位置;
獲取所有的引數之後,我們有必要將這個 ap 指標關掉,以免發生危險,方法是呼叫 va_end,他是輸入的引數 ap 置為 null,應該養成獲取完參數列之後關閉指標的習慣。
例如 int max(int n, ...); 其函式內部應該如此實現:
int max(int n, ...) { // 定參 n 表示後面變參數量,定界用,輸入時切勿搞錯
va_list ap; // 定義乙個 va_list 指標來訪問參數列
va_start(ap, n); // 初始化 ap,讓它指向第乙個變參,n之後的引數
int maximum = -0x7fffffff; // 這是乙個最小的整數
int temp;
for(int i = 0; i < n; i++) {
temp = va_arg(ap, int); // 獲取乙個 int 型引數,並且 ap 指向下乙個引數
if(maximum < temp) maximum = temp;
va_end(ap); // 善後工作,關閉 ap
return max;
// 在主函式中測試 max 函式的行為(c++ 格式)
int main() {
cout << max(3, 10, 20, 30) << endl;
cout << max(6, 20, 40, 10, 50, 30, 40) << endl;
3: 另乙個運用的例子
#define bufsize 80
char buffer[bufsize];
* 這個函式用來格式化帶引數的字串
int vspf(char *fmt, ...)
va_list argptr; //宣告乙個轉換引數的變數
int cnt;
va_start(argptr, fmt); //初始化變數
cnt = vsnprintf(buffer,bufsize ,fmt, argptr);//將帶引數的字串按照引數列**式化到buffer中
va_end(argptr); //結束變數列表,和va_start成對使用
return(cnt);
int main(int argc, char* argv)
int inumber = 30;
float fnumber = 90.0;
char string[4] = "abc";
vspf("%d %f %s", inumber, fnumber, string);
printf("%s/n", buffer);
return 0;
cnt = vsnprintf(buffer,bufsize ,fmt, argptr);//將帶引數的字串按照引數列**式化到buffer中
這個東東是關鍵。
以遊戲中的信件為例:
a 你的徒弟***公升到xx級了,你可以領取xx級的徒弟獎勵!
b 恭喜你達到xx級成功出師!你的師傅送你乙份大禮,趕緊查收吧!
那麼我們給信件模組的資訊就是:如果是a,則(a,***,xx); b ,則給(b,xx);
那麼信件模組給的統一介面sendletter需要多個不同的過載實現:
//fmt 為根據a或b等得到信件格式 如 你的徒弟%s公升到%d級了,你可以領取%d級的徒弟獎勵!
char content[size];
sendletter(lettertype type, char* str, int ivalue1, int ivalue2)
snprintf(content,sizeof(content)-1,fmt,str,ivalue1,ivalue2);
sendletter(lettertype type, char* str, int ivalue)
snprintf(content,sizeof(content)-1,fmt,str,ivalue);
看看如果我們用不定引數之後的介面是怎麼樣的:
sendletter(lettertype type, ...)
va_list argptr;
va_start(argptr, type);
vsnprintf(content,sizeof(content)-1,fmt,argptr);
va_end(argptr);
這樣我們就只需乙個介面了。
GO 不定引數的使用
語法格式 argumentname type e.g.package mainimport fmt func f1 args.int func main 語法格式 argumentname inte ce inte ce 空介面型別,因為任意型別都實現了空介面,因此可以將任意型別物件賦值給空介面。e...
不定引數的測試
使用不定引數時要注意 1 使用不定引數時,必須是引數的最後乙個,如 public static void paramtest string str,int nums 而public static void paramtest int nums,int x 是錯誤的 2 乙個方法只能有乙個不定長引數。...
預設引數結合不定長位置引數使用
需要注意的有兩點 1.當預設引數結合不定長位置引數一起使用的時候,需要把預設引數放到不定長位置引數 args 的後面 2.當不定長關鍵字引數結合其它引數一起使用的時候,那麼不定長關鍵字引數 kwargs 需要放到所有引數的最後面 當預設引數結合不定長位置引數一起使用的時候,需要把預設引數放到不定長位...