可變引數列表:
在我們一般經常使用的函式中,函式列出了期望接受的引數,但函式原型只能顯示固定的引數,那麼,如何讓一個函式在不同的時候接受不同數目的引數呢!使用可變引數列表就可實現,當一個函式事先不確定有多少個引數但是可以接受一個或多個引數,可以使得函式可以接受1個以上的任意多個引數。
可變引數列表是透過宏來實現的,這些宏定義在stdarg.h標頭檔案中,在這個標頭檔案中聲明瞭 一個型別va_list和三個宏va_start、va_arg、va_end配合使用,訪問引數的值。
可變引數函式的原型宣告格式為:type VAFunction(type arg1, type arg2, … ); 引數可以分為兩部分:個數確定的固定引數和個數可變的可選引數。函式至少需要一個固定引數,固定引數的宣告和普通函式一樣;可選引數由於個數不確定,宣告時用"…"表示。固定引數和可選引數共同構成一個函式的引數列表。
舉個例子理解:比方求指定個數的平均值
#include <stdio.h>#include <stdarg.h>int average(int n,...) //定義一個函式,實現可變引數{ va_list arg; //定義一個變數arg為va_list型別 int i = 0; int sum = 0; va_start(arg, n); for(i=0; i<n; i++) //迴圈獲取引數 { sum += va_arg(arg, int); } return sum/n; va_end(arg); }int main(){ int ret1 = 0; int ret2 = 0; ret1 = average(3, 1,2,3); ret2 = average(4, 2,3,3,5); printf("%d\n",ret1); printf("%d\n",ret2); return 0;}
程式結果
在vs中我們可以轉到定義處檢視各個型別和宏具體是怎樣實現的
1、首先va_list arg;
#elif defined(_M_IX86)#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )#define _crt_va_end(ap) ( ap = (va_list)0 )#elif defined(_M_IA64)
很明顯va_list就是一個型別重新命名;va_list實際上就是char*型,簡言之va_list arg就是聲明瞭一個字元型指標arg。
2、va_start(arg, n);
#elif defined(_M_IX86)#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )#define _crt_va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )#define _crt_va_end(ap) ( ap = (va_list)0 )#elif defined(_M_IA64)
_INTSIZEOF(n)整個做的事情就是將n的長度化為int長度的整數倍va_srart(ap,v)就是把上面的字元指標向後移動,跳過第一個引數n的地址。
3、va_arg(args, int)
#define _crt_va_end(ap) ( ap = (va_list)0 )
va_arg(args, int) 就是迴圈獲取到可變引數列表中的引數,args指向下一個引數地址,返回的則是當前引數地址。4、va_end(arg);
#define _crt_va_end(ap) ( ap = (va_list)0 )
當訪問完畢最後一個引數時,用VA_END宏結束可變引數的獲取。
可變引數列表的缺陷:
1、可變引數必須從頭到尾逐個訪問。如果你在訪問了幾個可變引數之後想半途終止,這是可以的,但是,如果你想一開始就訪問引數列表中間的引數,那是不行的
2、引數列表中至少有一個命名引數。如果連一個命名引數都沒有,就無法使用va_start。
3、如果在va_arg中指定了錯誤的型別,結果無法預測,因為在使用時,char、short、float型別的值實際上都作為int或double型別的值傳遞給函式。