日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

C,C++中使用可变参数

發布時間:2024/4/11 c/c++ 20 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C,C++中使用可变参数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

可變參數即表示參數個數可以變化,可多可少,也表示參數的類型也可以變化,可以是intdouble還可以是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’。如下例所示ij都將輸出12

???????int?i?= WriteLine("MoreWindows");

???????WriteLine("%d",?i);

???????int j =?printf("%s\n",?"MoreWindows");

???????WriteLine("%d", j);

?

第二步?如何處理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)41,2,3,4就取45,6,7,8就取8。對xn取整用C語言的算術表達就是((x+n-1)/n)*n,當n2的冪時可以將最后二步運算換成位操作——將最低?n - 1個二進制位清?0就可以了。

va_arg(ap,t)就是從ap中取出類型為t的數據,并將指針相應后移。如va_arg(ap,?int)就表示取出一個int數據并將指針向移四個字節。

因此在函數中先用va_start()得到變參的起始地址,再用va_arg()一個一個取值,最后再用va_end()收尾就可以解析可變參數了。

?

第三步?vfprintf()函數和vsprintf()函數

vfprintf()這個函數很重要,光從名字上看就知道它與經常使用的printf()函數有很大的關聯。它有多個重載版本,這里講解最常用的一種:

函數原型

int vfprintf(

???FILE *stream,

???const char *format,

???va_list argptr

);

第一個參數為一個FILE指針。FILE結構在C語言的讀寫文件必不可少。要對屏幕輸出傳入stdout

第二個參數指定輸出的格式。

第三個參數是va_list類型,這個少見,但其實就是一個char*表示可變參參數的起始地址。

返回值:成功返回輸出的字節數(不包括最后的’\0’),失敗返回-1

vsprintf()與上面函數類似,就只列出函數原型了:

int vsprintf(

???char *buffer,

???const char *format,

???va_list argptr

);

還有一個int _vscprintf(const char *format, va_list argptr );可以用來計算vsprintf()函數中的buffer字符串要多少字節的空間。

?

代碼范例

?下面就給出了自己實現的printf()函數(1)WriteLine()函數

int Printf(char *pszFormat, ...)?

{

???????va_list???pArgList;

??????

???????va_start(pArgList,?pszFormat);

???????int?nByteWrite?=?vfprintf(stdout,?pszFormat,?pArgList);

???????va_end(pArgList);

?

???????return?nByteWrite;

}

?

int WriteLine(char *pszFormat, ...)

{

???????va_list???pArgList;

??????

???????va_start(pArgList,?pszFormat);

???????int?nByteWrite?=?vfprintf(stdout,?pszFormat,?pArgList);

???????if (nByteWrite?!= -1)

??????????????putchar('\n'); //注2

???????va_end(pArgList);

??????

???????return (nByteWrite?== -1 ? -1 :?nByteWrite?+ 1);

}

調用與printf()函數相同。

再給出一個用可變參數來求和,遺憾的在C,C++中無法確定傳入的可變參數的個數(printf()中是通過掃描'%'個數來確實參數的個數的),因此要么就要指定個數,要么在參數的最后要設置哨兵數值:

設置哨兵數值:

const int?GUARDNUMBER?= 0;?//哨兵標識

//變參參數的個數無法確定,在printf()中是通過掃描'%'個數,在這通過設置哨兵標識來確定變參參數的終止

int MySum(int?i, ...)

{

???????int?sum?=?i;

???????va_list?argptr;

??????

???????va_start(argptr,?i);

???????while ((i?=?va_arg(argptr, int)) !=?GUARDNUMBER)

??????????????sum?+=?i;

???????va_end(argptr);

??????

???????return?sum;

}

可以這樣的調用:???printf("%d\n", MySum(1, 3, 5, 7, 9, 0));

但不可以直接傳入一個0???printf("%d\n", MySum(0)); //error

指定個數:

int MySum(int?nCount, ...)

{

???????if (nCount?<= 0)

??????????????return 0;

?

???????int?sum?= 0;

???????va_list?argptr;

??????

???????va_start(argptr,?nCount);

???????for (int?i?= 0;?i?<?nCount;?i++)

??????????????sum?+=?va_arg(argptr, int);

???????va_end(argptr);

??????

???????return?sum;

}

調用時第一個參數表示后面參數的個數如:

???????printf("%d\n", MySum(5, 1, 3, 5, 7, 9));

???????printf("%d\n", MySum(0));

代碼所用的頭文件:

#include?<stdarg.h>

#include?<stdio.h>

?

可變參數的使用方法遠遠不止上述幾種,不過在C,C++中使用可變參數時要小心,在使用printf()等函數時傳入的參數個數一定不能比前面的格式化字符串中的’%’符號個數少,否則會產生訪問越界,運氣不好的話還會導致程序崩潰。

?

可變參數的原形理涉及到調用函數時參數的入棧問題,這個下次再開一篇進行專門的探討。

?

?

?

注1.????網上有不用vfprintf()自己解析參數來實現printf()的,但很少能將功能做到與printf()相近(實際上能完全熟悉printf()的人已經就不多,不信的話可以先看看《C陷阱與缺陷》了解printf()很多不太常用的參數,再去Microsoft Visual Studio\VC98\CRT\SRC中查看OUTPUT.Cprintf()的實現)。

注2.????如果輸出單個字符?putchar(ch)會比printf(“%c”, ch)效率高的多。在字符串不長的情況下,多次調用putchar()也會比調用printf(“%s\n”, szStr);的效率高。在函數大量調用時非常明顯。

總結

以上是生活随笔為你收集整理的C,C++中使用可变参数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。