不定引數的使用

2021-05-25 04:51:53 字數 3165 閱讀 7945

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 需要放到所有引數的最後面 當預設引數結合不定長位置引數一起使用的時候,需要把預設引數放到不定長位...