C++編程中實現可變參數函數有多種途徑,本文介紹一種最常見的實現途徑,即可變參數宏方法:形參生命為省略符,函數實現時用參數列表宏訪問參數。
1. 可變參數宏實現變參函數
可變參數宏實現可分為以下幾個步驟:
函數形參原型中給出省略符;
函數實現中聲明一個va_list可變參數列表變量;
開始初始化構造va_list變量;
訪問變參列表;
完成清理工作;
上述步驟的實現需要使用到四個宏:
1、va_list
2、void va_start(va_list ap, last_arg)
3、type va_arg (va_list ap, type)
4、void va_end(va_list ap)
va_list 是在C語言中解決變參問題的一組宏
void va_start(va_list ap, last_arg)
ap :是一個 va_list 類型的對象,它用來存儲通過 va_arg 獲取額外參數時所必需的信息。
last_arg :是最后一個傳遞給函數的已知的固定參數,即省略號之前的參數。
宏定義:type va_arg (va_list ap, type)
該宏用於變參數函數調用過程中,type是當前參數類型,調用該宏后,ap指向變參數列表中的下一個參數,返回ap指向的參數值,是一個類型為type的表達式。 ap是arg_ptr參數指針之意。
void va_end(va_list ap)
允許使用了 va_start 宏的帶有可變參數的函數返回。如果在從函數返回之前沒有調用 va_end,則結果為未定義。
這些宏在頭文件stdarg.h中聲明定義。因此使用時需要包含該頭文件。
下面給出用法示例:
#include <stdarg.h>
//可變參數函數sum(),求任意個數整數的和。
//Step1: 函數形參原型中給出省略符
int Sum(int count, ...);
int Sum(int count, ...) {
//Step2: 函數實現中聲明一個va_list可變參數列表變量;
va_list ap;
//Step3: 開始初始化構造va_list變量, 第二個參數為最后一個確定的形參
va_start(ap, count);
int sum = 0;
for(int i = 0; i < count; i++) {
//讀取可變參數,的二個參數為可變參數的類型
sum += va_arg(ap, int);
}
//清理工作
va_end(ap);
return sum;
}
實際中使用可變參數宏實現C++可變參數函數編程,還要注意一下幾點:
函數原型中省略號必須在參數列表的末尾:也就是說,在函數原型中參數列表省略號的右邊不能再出現確定參數;
試用完成是用va_end做清理工作步驟不可缺少,否則可能導致內存或資源泄漏;
va_list在一次訪問中不能后退,但可以多次構造va_list多次訪問;
2. 更安全的可變參數函數實現方法
對於上面示例代碼中count傳進的實參如果與后面...省略符對應的實際參數數量不一致時,可能導致函數風險。這一切完全依賴運行時的具體情況而定,很不安全。
另一種更安全的可變參數宏實現方法是利用C++的attribute((format()))特性來輔助可變參數的檢查。
最常見的形式是有如下兩個:
__attribute__((format(printf, m, n)))
__attribute__((format(scanf, m, n)))
其中參數m與n的含義為:
m:第幾個參數為格式化字符串(format string);
n:參數集合中的第一個,即參數“…”里的第一個參數在函數參數總數排在第幾;
attributeformat屬性可以給被聲明的函數加上類似printf或者scanf的特征,它可以使編譯器檢查函數聲明和函數實際調用參數之間的格式化字符串是否匹配。format屬性告訴編譯器,按照printf, scanf等標准C函數參數格式規則對該函數的參數進行檢查。這在我們自己封裝調試信息的接口時非常的有用。
format的語法格式為:
format (archetype, string-index, first-to-check)
其中,“archetype”指定是哪種風格;“string-index”指定傳入函數的第幾個參數是格式化字符串;“first-to-check”指定從函數的第幾個參數開始按上述規則進行檢查。
下面給出2個示例:
一般函數
為自己定義的一個帶有可變參數的函數,其功能類似於printf:
extern void myprint(const char *format,...)attribute((format(printf,1,2))); //m=1;n=2
extern void myprint(int l,const char *format,...)attribute((format(printf,2,3))); //m=2;n=3
類成員函數
需要特別注意的是,如果myprint是一個函數的成員函數,那么m和n的值可有點“懸乎”了,例如:
extern void myprint(int l,const char *format,...)attribute((format(printf,3,4)));
其原因是,類成員函數的第一個參數實際上一個隱身的this指針。
最后,如果你也想成為程序員,想要快速掌握編程,趕緊加入學習企鵝圈子!
里面有資深專業軟件開發工程師,在線解答你的所有疑惑~編程語言入門“so easy”
編程學習書籍:
編程學習視頻:
