名字
stdarg, va_start, va_arg, va_end, va_copy可變參數列表。
概要
#include <stdarg.h>
void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);
描述
一個函數可能有不同數量的不同類型的參數。頭文件<stdarg.h>聲明瞭一個va_list類型,並且定義3個宏來處理被調函數的參數列表。被調函數必須聲明一個被va_start, va_arg和va_end使用的類型爲va_list的對象。
va_start()
va_start()宏初始化一個被後面va_arg()和va_end()使用的ap,並且必須率先調用。last是在可變參數列表之前的最後一個我們知道類型的參數的名字。因爲va_start()宏可能會用到last的地址,所以不要把last聲明成寄存器變量或者函數、數組類型。
va_arg()
va_arg()這個宏擴展到調用中的下一個參數的值和類型。ap就是由va_start()初始化的va_list類型的ap。每次調用va_arg(),它都會修改ap,使它的下一次調用返回下一個參數。參數type就是一個類型名,這樣就可以獲取一個類型對象了。
在調用va_start()之後第一次調用va_arg()它就返回last後面的第一個參數。後面的調用,依次返回剩下的參數的值。
如果後面沒有其它參數了,或者type和實際的下一個參數不匹配(會按照默認參數擴展?),就會產生隨機的錯誤。
如果ap在調用va_arg(ap, type)的時候是指向一個函數的,那麼在這個函數返回的時候ap的值是未定義的。
va_end()
每次在一個函數裏面調用va_start(),必須有一個va_end()與它進行匹配。用多對va_start()和va_end()多次遍歷一個va_list也是可以的。va_end()可以是一個宏或者一個函數。
va_copy()
va_copy()將之前初始化的可變參數列表從src拷貝到dest。這個行爲等價於先對dest做va_start(),然後調用獲得src相同次數的va_arg()。
一個顯而易見的實現是讓va_list指向可變函數的棧幀。這樣的話(目前最常見),這樣的話,賦值操作好像也沒什麼問題。
va_list aq = ap;
不幸的是,有些系統也把va_list實現成一個指針數組。那這樣的話就需要:
va_list aq;
*aq = *ap;
最後,對於參數傳遞到寄存器的系統,就有必要用va_start()分配內存,存儲參數,並且表明哪個參數是下一個。這樣的話va_arg()纔可以對參數進行遍歷。然後va_end()可以釋放內存。爲了解決這個問題,C99添加了一個宏va_copy()。那麼上面的賦值就可以用下面的函數代替:
va_list aq;
va_copy(aq, ap);
...
va_end(aq);
每次調用va_copy(),必須在同一個函數裏面調用對應的va_end()。有些系統不提供va_copy(),但是有__va_copy,這個是草案裏面使用的名字。
屬性
這部分使用的術語的解釋可以參看attribute(7)。
接口 | 屬性 | 值 |
va_start(), va_end(), va_copy() | 線程安全性 | MT-safe |
va_arg() | 線程安全性 | MT-safe race:ap |
規範
va_start(), va_arg(), va_end()宏遵守C89,va_copy()遵守C99。
註釋
這些宏和它們代替的歷史上的宏並不兼容。一個後向兼容的版本可以在頭文件<varargs.h>中找到。
歷史的背景是:
#include <varargs.h>
void
foo(va_alist)
va_dcl
{
va_list ap;
va_start(ap);
while (...) {
...
x = va_arg(ap, type);
...
}
va_end(ap);
}
有些系統,va_end包含一個“}”,va_start包含一個“{”,這樣的話兩個宏就必須出現在同一個函數裏面,否則編譯就不會通過。
BUGS
不像varargs宏,stdarg宏不允許程序員編碼一個沒有固定參數的函數。這個問題主要是在將varargs代碼轉換爲stdarg代碼的時候引入了很多工作量。當然,這個也給從可變參數函數內部將它所有的參數傳遞給一個有一個va_list參數的函數(比如vfprintf(3))帶來了很多的困難。
例子
函數foo把一個格式字符串作爲參數,並且根據每個格式字符表明的類型打印出每個參數。
#include <stdio.h>
#include <stdarg.h>
void
foo(char *fmt, ...)
{
va_list ap;
int d;
char c, *s;
va_start(ap, fmt);
while(*fmt)
switch(*fmt++) {
case 's': /* string */
s = va_arg(ap, char *);
printf("string %s\n", s);
break;
case 'd': /* int */
d = va_arg(ap, int);
printf("int %d\n", d);
break;
case 'c': /* char */
/* Need a cast hear since va_arg only
takes fully promoted types */
c = (char) va_arg(ap, int);
printf("char %c\n", c);
break;
}
va_end(ap);
}
版權
本頁是Linux man-page項目4.15版的一部分。關於項目的描述,報bug相關信息,以及本頁的最新版本可以在https://www.kernel.org/doc/man-pages/找到。