stdarg.h
stdarg.h
是C語言中C標準函式庫的標頭檔,stdarg是由standard(標準) arguments(參數)簡化而來,主要目的為讓函式能夠接收不定量參數。[1] C++的cstdarg
標頭檔中也提供這樣的機能;雖然與C的標頭檔是相容的,但是也有衝突存在。
不定參數函式(Variadic functions)是stdarg.h
內容典型的應用,雖然也可以使用在其他由不定參數函式呼叫的函式(例如,vprintf
)。
宣告不定參數函式
編輯不定參數函式的參數數量是可變動的,它使用省略號來忽略之後的參數。例如printf
函式一般。代表性的宣告為:
int check(int a, double b, ...);
不定參數函式最少要有一個命名的參數,所以
char *wrong(...);
在C是不被允許的。在C,省略符號之前必須要有逗號;在C++,則沒有這種強制要求。
(雖然在C++中,這樣的宣告是合理的,但是這種寫法,因為沒有已命名的參數,使得va_start
沒辦法抓到動態參數的正確起始點。)
定義不定參數函式
編輯使用與聲明時相同的語法來定義:
long func(char, double, int, ...);
long func(char a, double b, int c, ...)
{
/* ... */
}
在舊形式中可能會出現較省略的函式定義:
long func();
long func(a, b, c, ...)
char a;
double b;
{
/* ... */
}
stdarg.h
型態
編輯
名稱 | 描述 | 相容 |
---|---|---|
va_list |
用來儲存宏va_arg與宏va_end所需資訊 | C89 |
stdarg.h
巨集
編輯
名稱 | 描述 | 相容 |
---|---|---|
va_start |
使va_list 指向起始的參數 |
C89 |
va_arg |
檢索參數 | C89 |
va_end |
釋放va_list |
C89 |
va_copy |
拷貝va_list 的內容 |
C99 |
存取參數
編輯存取未命名的參數,首先必須在不定參數函式中宣告va_list
型態的變數。呼叫va_start
並傳入兩個參數:第一個參數為va_list
型態的變數,第二個參數為函式的動態參數前面最後一個已命名的參數名稱,接着每一呼叫va_arg
就會回傳下一個參數,va_arg
的第一個參數為va_list
,第二個參數為回傳的型態。最後va_end
必須在函式回傳前被va_list
呼叫(當作參數)。(沒有要求要讀取完所有參數)
C99提供額外的巨集,va_copy
,它能夠複製va_list
。而va_copy(va2, va1)
意思為拷貝va1
到va2
。
沒有機制定義該怎麼判別傳遞到函式的參數量或者型態。函式通常需要知道或確定它們變化的方法。共通的慣例包含:
型別安全性
編輯有些C的實現,提供了對不定參數的擴充,允許編譯器檢查適當的格式化字串及標志(sentinels)的使用。如果沒有這種擴充,編譯器通常無從檢查傳入函式的未命名參數是否為所預期的型態,也不能轉換它們為所需要的型態。因此,必須小心謹慎以確保正確性,因為不匹配的型態降到導致未定義行為(Undefined behavior)。例如,如果傳遞空指標,不能僅僅寫入NULL
(可能實際定義為0),還要轉化為(cast)適當的指標類型。另一個考慮是未命名參數的預設的類型提升。float
將會自動的被轉換成double
‧同樣的比int
(整數)更小容量的參數型態將會被轉換成int
或者unsigned int
‧函式所接收到的未命名參數必須預期將被型態提升。
例子
編輯#include <stdio.h>
#include <stdarg.h>
void printargs(int arg1, ...) /* 輸出所有int型態的參數,直到-1結束 */
{
va_list ap;
int i;
va_start(ap, arg1);
for (i = arg1; i != -1; 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
varargs.h
編輯
POSIX定義所遺留下的標頭檔varargs.h
,它早在C標準化前就已經開始使用了且提供類似stdarg.h
的機能。MSDN明確指出這一標頭檔已經過時,完全被stdarg.h取代[2]。這個標頭檔不屬於ISO C的一部分。檔案定義在單一UNIX規範的第二個版本中,簡單的包含所有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 /* 這裡沒有分號! */
{
va_list ap;
int i = 0;
va_start(ap);
for (; n; n--)
i += va_arg(ap, int);
va_end(ap);
return i;
}
以及相同的呼叫方法。
varargs.h
因為運作的模式需要舊型態的函式定義。[3]
參見
編輯參考
編輯- ^ IEEE Std 1003.1
stdarg.h
. [2009-07-04]. (原始內容存檔於2009-04-11). - ^ The macros defined in VARARGS.H are deprecated and exist solely for backwards compatibility. Use the macros defined in STDARGS.H unless you are working with code before the ANSI standard.
- ^ 單使用者UNIX系統規範(Single UNIX Specification)
varargs.h
. [2007-08-01]. (原始內容存檔於2008-06-18).