stdarg.hC語言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)意思為拷貝va1va2

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

  • 使用printfscanf類的格式化字串來嵌入明確指定的型態。
  • 在不定參數最後的標記值(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]

參見

編輯

參考

編輯
  1. ^ IEEE Std 1003.1 stdarg.h. [2009-07-04]. (原始內容存檔於2009-04-11). 
  2. ^ 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.
  3. ^ 單使用者UNIX系統規範(Single UNIX Specification) varargs.h. [2007-08-01]. (原始內容存檔於2008-06-18).