可變參數即表示參數個數可以變化,可多可少,也表示參數的類型也可以變化,可以是int,double還可以是char*,類,結構體等等。可變參數是實現printf(),sprintf()等函數的關鍵之處,也可以用可變參數來對任意數量的數據進行求和,求平均值帶來方便(不然就用數組或每種寫個重載)。在C#中有專門的關鍵字parame,但在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。
同樣在<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 *stream,
const char *format,
va_list argptr
);
char *buffer,
const char *format,
va_list argptr
);
代碼范例
下面就給出了自己實現的printf()函數(注1)與WriteLine()函數
代碼所用的頭文件:
#include <stdarg.h>
#include <stdio.h>
可變參數的使用方法遠遠不止上述幾種,不過在C,C++中使用可變參數時要小心,在使用printf()等函數時傳入的參數個數一定不能比前面的格式化字符串中的'%'符號個數少,否則會產生訪問越界,運氣不好的話還會導致程序崩潰。
可變參數的原形理涉及到調用函數時參數的入棧問題,這個下次再開一篇進行專門的探討。
注1.網上有不用vfprintf()自己解析參數來實現printf()的,但很少能將功能做到與printf()相近(實際上能完全熟悉printf()的人已經就不多,不信的話可以先看看《C陷阱與缺陷》了解printf()很多不太常用的參數,再去Microsoft Visual Studio/VC98/CRT/SRC中查看OUTPUT.C對printf()的實現)。
注2.如果輸出單個字符 putchar(ch)會比printf(“%c”, ch)效率高的多。在字符串不長的情況下,多次調用putchar()也會比調用printf(“%s/n”, szStr);的效率高。在函數大量調用時非常明顯。
新聞熱點
疑難解答
圖片精選