

C語言可變參數函數
C 語言允許定義參數數量可變的函數,這稱為可變參數函數。這種函數需要固定數量的強制參數,後面是數量可變的可選參數。這種函數必須至少有一個強制參數。可選參數的類型可以變化。可選參數的數量由強制參數的值決定,或由用來定義可選參數清單的特殊值決定。
C 語言中最常用的可變參數函數例子是 printf()和 scanf()。這兩個函數都有一個強制參數,即格式化字元串。格式化字元串中的轉換修飾符決定了可選參數的數量和類型。對于每一個強制參數來說,函數頭部都會顯示一個适當的參數,像普通函數聲明一樣。參數清單的格式是強制性參數在前,後面跟着一個逗号和省略号(...),這個省略号代表可選參數。
可變參數函數要擷取可選參數時,必須通過一個類型為 va_list 的對象,它包含了參數資訊。這種類型的對象也稱為參數指針(argument pointer),它包含了棧中至少一個參數的位置。可以使用這個參數指針從一個可選參數移動到下一個可選參數,由此,函數就可以擷取所有的可選參數。va_list 類型被定義在頭檔案 stdarg.h 中。


如何實作
C語言可變參數通過三個宏(va_start、va_end、va_arg)和一個類型(va_list)實作的
- void va_start ( va_list ap, paramN );功能:初始化可變參數清單
- void va_end ( va_list ap );功能:關閉初始化清單(将 ap 置空)。
- type va_arg ( va_list ap, type );功能:傳回下一個參數的值。
好了,綜合上面3個宏和一個類型可以猜出如何實作C語言可變長參數函數:
va_start 擷取參數清單(的位址)存儲到 ap 中,va_arg 逐個擷取值,最後用 va_arg 将 ap 置空。


小試牛刀
#include #include #define END -1int va_sum(int first_num, ...){ // (1) 定義參數清單 va_list ap; // (2) 初始化參數清單 va_start(ap, first_num); int result = first_num; int temp = 0; // 擷取參數值 while ((temp = va_arg(ap, int))!=END){ result += temp; } // 關閉參數清單 va_end(ap); return result;}int main(){ printf("%d\n", va_sum(1, 2, 3, 4,END)); printf("%d\n", va_sum(1, 2, 3, 4, 5, END)); return 0;}


注意項
- 宏定義在 stdarg.h 中,是以使用時,不要忘了添加頭檔案。
- 設定一個參數結束标志(cplusplus 上說,va_arg 并不能确定哪個參數是最後一個參數)。
- 類型的比對


漸入佳境
我們一起來偷看下“内褲”,發現标準庫中是這樣聲明printf函數的。
最終你要學習就是這個:int printf(const char * format, ...);下面我們來寫一個簡單的可變參數的C 函數printf函數
#include
#include
#include
#include
int myvfprintf(FILE* stream, const char* format, va_list arglist){
int translating = 0;
int ret = 0; //記錄最終輸出的字元個數
const char* p = 0;
for (p = format; *p != '\0'; ++p)
{
switch (*p)
{
case '%':
if (!translating)
{
translating = 1; //translating置為1,代表後面的字元需要解析
}
else
{
if (fputc('%', stream) < 0)
return EOF;
++ret;
translating = 0;
}
break;
case 'd':
if (translating) //%d
{
char buf[16];
translating = 0;
_itoa_s(va_arg(arglist, int), buf, 10);
if (fputs(buf, stream) < 0)
return EOF;
ret += strlen(buf);
}
else if (fputc('d', stream) < 0)
return EOF;
else
++ret;
break;
case 's':
if (translating)
{
const char* str = va_arg(arglist, const char*);
translating = 0;
if (fputs(str, stream) < 0)
return EOF;
ret += strlen(str);
}
else if (fputc('s', stream) < 0)
return EOF;
else
++ret;
break;
default:
if (translating)
translating = 0;
if (fputc(*p, stream) < 0)
return EOF;
else
++ret;
break;
}
}
return ret;
}
int myprintf(const char* format, ...){
va_list(arglist);
va_start(arglist, format);
return myvfprintf(stdout, format, arglist);
}
int main(){
printf("%s\n", "ILoveyou");
myprintf("%s\n", "ILoveyou");
return 0;
}
運作測試結果:


結束語
文章都是手打原創,每天最淺顯的介紹C語言、C++,windows知識,喜歡我的文章就關注一波吧,每天帶你學習C/C++不同的知識,也可以看到最新更新和之前發表的文章哦。如果今天學到知識的,可以在留言區留言學到了哦,如果喜歡可以收藏,轉發,評論哦,這真的對我很重要!!