當我們編寫一個變量函數的時候,可能會遇到很多問題,因此在不必要的情況下不需要使用變量參數。如果在C++中,我們應該使用C++的多態性來實現可變參數的功能,今天武林技術頻道小編將著重講解C/C++可變參數的使用方法。
第一步 可變參數表示
用三個點…來表示,查看printf()函數和scanf()函數的聲明:
int printf(const char *, ...);
int scanf(const char *, ...);
這三個點用在宏中就是變參宏(Variadic Macros),默認名稱為__VA_ARGS__。如:
#define WriteLine(...) { printf(__VA_ARGS__); putchar('/n');}
再WriteLine("MoreWindows");
考慮下printf()的返回值是表示輸出的字節數。將上面宏改成:
#define WriteLine (...) printf(__VA_ARGS__) + (putchar('/n') != EOF ? 1: 0);
這樣就可以得到WriteLine宏的返回值了,它將返回輸出的字節數,包括最后的'/n'。如下例所示i和j都將輸出12。
第二步 如何處理va_list類型
函數內部對可變參數都用va_list及與它相關的三個宏來處理,這是實現變參參數的關鍵之處。
?
在<stdarg.h>中可以找到va_list的定義:
typedef char *? va_list;
再介紹與它關系密切的三個宏要介紹下:va_start(),va_end()和va_arg()。
同樣在<stdarg.h>中可以找到這三個宏的定義:
#define va_start(ap,v)? ( ap = (va_list)&v + _INTSIZEOF(v) )
#define va_end(ap)????? ( ap = (va_list)0 )
#define va_arg(ap,t)??? ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
其中用到的_INTSIZEOF宏定義如下:
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
來分析這四個宏:
va_end(ap)這個最簡單,就是將指針置成NULL。
va_start(ap,v)中ap = (va_list)&v + _INTSIZEOF(v)先是取v的地址,再加上_INTSIZEOF(v)。_INTSIZEOF(v)就有點小復雜了。( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )全是位操作,看起來有點麻煩,其實不然,非常簡單的,就是取整到sizeof(int)。比如sizeof(int)為4,1,2,3,4就取4,5,6,7,8就取8。對x向n取整用C語言的算術表達就是((x+n-1)/n)*n,當n為2的冪時可以將最后二步運算換成位操作——將最低 n - 1個二進制位清 0就可以了。
va_arg(ap,t)就是從ap中取出類型為t的數據,并將指針相應后移。如va_arg(ap, int)就表示取出一個int數據并將指針向移四個字節。
因此在函數中先用va_start()得到變參的起始地址,再用va_arg()一個一個取值,最后再用va_end()收尾就可以解析可變參數了。
第三步 vfprintf()函數和vsprintf()函數
vfprintf()這個函數很重要,光從名字上看就知道它與經常使用的printf()函數有很大的關聯。它有多個重載版本,這里講解最常用的一種:
函數原型
第一個 參數為一個FILE指針。FILE結構在C語言的讀寫文件必不可少。要對屏幕輸出傳入stdout。
第二個 參數指定輸出的格式。
第三個 參數是va_list類型,這個少見,但其實就是一個char*表示可變參參數的起始地址。
返回值:成功返回輸出的字節數(不包括最后的'/0'),失敗返回-1。
vsprintf()與上面函數類似,就只列出函數原型了:
?
?
還有一個int _vscprintf(const char *format, va_list argptr );可以用來計算vsprintf()函數中的buffer字符串要多少字節的空間。
代碼范例
下面就給出了自己實現的printf()函數(注1)與WriteLine()函數
?
?
調用與printf()函數相同。
再給出一個用可變參數來求和,遺憾的在C,C++中無法確定傳入的可變參數的個數(printf()中是通過掃描'%'個數來確實參數的個數的),因此要么就要指定個數,要么在參數的最后要設置哨兵數值:
設置哨兵數值:
?
?
可以這樣的調用:?? printf("%d/n", MySum(1, 3, 5, 7, 9, 0));
但不可以直接傳入一個0:?? printf("%d/n", MySum(0)); //error
指定個數:
?
?
調用時第一個參數表示后面參數的個數如:
?
?
代碼所用的頭文件:
#include <stdarg.h>
#include <stdio.h>
變量參數的使用遠不止上述的介紹,但是在C和C++中使用變量參數時要小心。使用printf()和其他函數時,參數的數目不得小于前面格式化字符串中的“%”符號的數目。
新聞熱點
疑難解答
圖片精選