C++編程中實現可變參數函數有多種途徑,本文介紹一種最多見的實現途徑,便可變參數宏方法:形參生命爲省略符,函數實現時用參數列表宏訪問參數。程序員
可變參數宏實現可分爲如下幾個步驟:編程
函數形參原型中給出省略符;安全
函數實現中聲明一個va_list可變參數列表變量;編程語言
開始初始化構造va_list變量;函數
訪問變參列表;學習
完成清理工做;this
上述步驟的實現須要使用到四個宏:指針
一、va_list調試
二、void va_start(va_list ap, last_arg)orm
三、type va_arg (va_list ap, type)
四、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屢次訪問;
對於上面示例代碼中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」
編程學習書籍:
編程學習視頻: