stdarg.h
C标准函数库 |
---|
一般 |
杂项 |
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).