1. 程式人生 > >stdarg.h標頭檔案詳解

stdarg.h標頭檔案詳解

stdarg.hC語言C標準函式庫標頭檔,stdarg是由stdandard(標準) arguments(引數)簡化而來,主要目的為讓函式能夠接收不定量引數。[1]C++cstdarg標頭檔中也提供這樣的機能;雖然與C的標頭檔是相容的,但是也有衝突存在。

不定引數函式(Variadic functions)是stdarg.h內容典型的應用,雖然也可以使用在其他由不定引數函式呼叫的函式(例如,)。

宣告不定引數函式

不定引數函式的引數數量是可變動的,它使用省略號來忽略之後的引數。例如函式一般。代表性的宣告為:

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; { /* ... */ }

[編輯]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)意思為拷貝va1va2

沒有機制定義該怎麼判別傳遞到函式的引數量或者資料型別。函式通常需要知道或確定它們變化的方法。共通的慣例包含:

  • 使用或scanf類的格式化字串來嵌入明確指定的資料型別。
  • 在不定引數最後的標記值(sentinel value)。
  • 總數變數來指明不定引數的數量。

[編輯]型別安全性

有些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]


轉自http://my.oschina.net/wxwHome/blog/51312