C 程式設計/stdarg.h
stdarg.h 是一個頭檔案,它允許函式接受不定數量的引數。[1] 它提供了遍歷未知數量和型別的函式引數列表的功能。
stdarg.h 的內容通常用於可變引數函式,儘管它們也可以在其他函式(例如,vprintf)中使用,這些函式由可變引數函式呼叫。
可變引數函式是可以接受可變數量引數的函式,它們在最後一個引數的位置用省略號(...)宣告。printf 函式就是一個這樣的例子。典型的宣告如下:
int check(int a, double b, ...);
可變引數函式必須至少有一個命名引數,因此,例如,
char *wrong(...);
在 C 中是不允許的。(在 C++ 中,這種宣告是允許的,但不太有用。)在 C 中,逗號必須在省略號之前;在 C++ 中,它是可選的。
在定義中使用相同的語法
long func(char, double, int, ...);
long func(char a, double b, int c, ...)
{
/* ... */
}
省略號也可以出現在舊式函式定義中
long func();
long func(a, b, c, ...)
char a;
double b;
{
/* ... */
}
| 名稱 | 描述 | 相容性 |
|---|---|---|
va_list |
用於迭代引數的型別 | C89 |
| 名稱 | 描述 | 相容性 |
|---|---|---|
va_start |
使用 va_list 開始迭代引數 |
C89 |
va_arg |
檢索引數 | C89 |
va_end |
釋放 va_list |
C89 |
va_copy |
將一個 va_list 的內容複製到另一個 va_list |
C99 |
要訪問未命名的引數,必須在可變引數函式中宣告一個 va_list 型別的變數。然後,使用兩個引數呼叫 va_start 宏:第一個引數是宣告為 va_list 型別的變數,第二個引數是函式最後一個命名引數的名稱。在此之後,每次呼叫 va_arg 宏都會產生下一個引數。va_arg 的第一個引數是 va_list,第二個引數是傳遞給函式的下一個引數的型別。最後,在函式返回之前,必須對 va_list 呼叫 va_end 宏。(不需要讀取所有引數。)
C99 提供了一個額外的宏,va_copy,它可以複製 va_list 的狀態。宏呼叫 va_copy(va2, va1) 將 va1 複製到 va2 中。
沒有定義用於確定傳遞給函式的未命名引數的數量或型別的機制。函式只需知道或確定這一點,方法各不相同。常見約定包括
- 使用包含指示引數型別的嵌入式說明符的
printf或scanf風格的格式字串。 - 可變引數結尾處的哨兵值。
- 指示可變引數數量的計數引數。
一些 C 實現提供了 C 擴充套件,允許編譯器檢查格式字串和哨兵的正確使用。除了這些擴充套件,編譯器通常無法檢查傳遞的未命名引數是否為函式期望的型別,或者將它們轉換為所需的型別。因此,應注意確保這方面的正確性,因為如果型別不匹配,會導致未定義的行為。
例如,如果呼叫 va_arg(ap, short *),則必須在呼叫中將該引數的空指標傳遞為 (short *)0;使用 NULL 是不正確的,因為指向不同資料型別的指標可能具有不同的尺寸。(short *)NULL 可以用作替代,但沒有任何作用,並且可能會誤導不經意的讀者,使其認為不需要強制轉換。
另一個需要考慮的是應用於未命名引數的預設引數提升。float 會自動提升為 double。同樣,比 int 窄的型別的引數將提升為 int 或 unsigned int。接收未命名引數的函式必須期望提升後的型別。
GCC 具有一個擴充套件,用於檢查傳遞的引數
format(archetype, string-index, first-to-check)- 格式屬性指定函式採用
printf、scanf、strftime或strfmon風格的引數,這些引數應針對格式字串進行型別檢查。例如,宣告導致編譯器檢查對extern int my_printf (void *my_object, const char *my_format, ...) __attribute__ ((format (printf, 2, 3)));my_printf的呼叫中的引數,以確保與printf風格的格式字串引數my_format一致。—"5.27 Extensions to the C Language Family - Declaring Attributes of Functions". Retrieved 2009-01-03.
#include <stdio.h>
#include <stdarg.h>
/* print all non-negative args one at a time;
all args are assumed to be of int type */
void printargs(int arg1, ...)
{
va_list ap;
int i;
va_start(ap, arg1);
for (i = arg1; i >= 0; i = va_arg(ap, int))
printf("%d ", i);
va_end(ap);
putchar('\n');
}
int main(void)
{
printargs(5, 2, 14, 84, 97, 15, 24, 48, -1);
printargs(84, 51, -1);
printargs(-1);
printargs(1, -1);
return 0;
}
此程式輸出以下結果
5 2 14 84 97 15 24 48 84 51 1
要在您的函式內呼叫其他可變引數函式(例如 sprintf),您需要使用該函式的可變引數版本(在此例中為 vsprintf)
void MyPrintf(const char* format, ...)
{
va_list args;
char buffer[BUFSIZ];
va_start(args,format);
vsprintf (buffer, format, args );
FlushFunnyStream(buffer);
va_end(args);
}
POSIX 定義了遺留標頭檔案 varargs.h,它早於 C 的標準化,並提供與 stdarg.h 相似的功能。此標頭檔案不是 ISO C 的一部分。該檔案,如 Single UNIX Specification 的第二個版本中定義的那樣,僅包含 C89 stdarg.h 的所有功能,但有以下例外:它不能在標準 C 的新式定義中使用;您可以選擇不包含給定的引數(標準 C 要求至少有一個引數);並且它的工作方式不同 - 在標準 C 中,您會編寫
#include <stdarg.h>
int summate(int n, ...)
{
va_list ap;
int i = 0;
va_start(ap, n);
for (; n; n--)
i += va_arg(ap, int);
va_end(ap);
return i;
}
或者使用舊式函式定義
#include <stdarg.h>
int summate(n, ...)
int n;
{
/* ... */
}
並使用以下方式呼叫
summate(0);
summate(1, 2);
summate(4, 9, 2, 3, 2);
使用 varargs.h,函式將是
#include <varargs.h>
summate(n, va_alist)
va_dcl /* no semicolon here! */
{
va_list ap;
int i = 0;
va_start(ap);
for (; n; n--)
i += va_arg(ap, int);
va_end(ap);
return i;
}
並且使用相同的方式呼叫。
varargs.h 要求使用舊式函式定義,因為實現方式不同。[2]
- ↑ "IEEE Std 1003.1
stdarg.h". 檢索於 2009-07-04. - ↑ "Single UNIX Specification
varargs.h". 檢索於 2007-08-01.