为什么C语言不允许使用可变长度参数列表的函数,例如:
void f(...)
{
// do something...
}
为什么C语言不允许使用可变长度参数列表的函数,例如:
void f(...)
{
// do something...
}
va_start
的一致性。为了方便实现,va_start
获取最后一个命名参数的名称。根据典型的变长参数调用约定和参数存储方向,va_arg
将在地址(¶meter_name) + 1
或(first_vararg_type*)(¶meter_name) - 1
(加减一些填充以确保对齐)处找到第一个vararg。va_start
,它必须直接从堆栈指针中获取第一个vararg(或者严谨地说,从帧指针中获取,因为函数中的代码很可能已经移动了sp)。原则上这是可行的 -- 任何实现都应该以某种方式访问堆栈[*],在某个层面上 -- 但这可能会令一些实施者感到恼火。一旦您知道了可变参数调用约定,通常可以实现va_
宏而无需任何其他实现特定的知识,而这也需要知道如何直接获取调用参数。我之前在仿真层中实现了这些可变参数宏,这让我感到很烦。printf
和朋友们中,第一个参数的值告诉函数varargs的类型以及它们的数量。我认为在理论上,被调用的函数可以查看某些全局变量来确定如何读取第一个参数(以及是否有参数),但这样做相当麻烦。我肯定不会特意支持这种操作,并且增加一个新版本的va_start
会给实现带来额外的负担。
[*]或者如果实现不使用堆栈,则使用其他方式传递函数参数。
<varargs.h>
(标准设施的前身和启发者)仅允许可变参数。C语言的理念指出,在可变参数之前具有固定类型参数的可能性是标准化过程的一项创新。我还要注意到,C++允许没有固定类型参数,并且我所见过的唯一用途是在模板元编程上下文中。 - AProgrammer使用可变长度参数列表时,您必须声明第一个参数的类型-这是语言的语法要求。
void f(int k, ...)
{
/* do something */
}
这将完美地工作。然后,您需要在函数内部使用va_list
、va_start
、va_end
等来访问各个参数。
C语言确实允许使用可变长度参数,但需要使用va_list、va_start、va_end等。你认为printf和相关函数是如何实现的呢?尽管如此,我仍建议不要使用它。通常情况下,你可以使用数组或结构体更清晰地完成类似的操作。
va_start
需要一个函数参数名。 - lhf玩弄它,我做了一个不错的实现,我认为一些人可能会考虑使用它。
template<typename T>
void print(T first, ...)
{
va_list vl;
va_start(vl, first);
T temp = first;
do
{
cout << temp << endl;
}
while (temp = va_arg(vl, T));
va_end(vl);
}
在编程中,C语言没有内在原因不能接受void f(...)。它本来可以这样做,但是C特性的“设计者”决定不这样做。
我猜测他们这样做的动机是,允许void f(...)需要更多的“隐藏”代码(可以视为运行时),而不允许会更少:为了区分f()和f(arg)(以及其他情况),C应该提供一种计算参数个数的方法,这需要更多生成的代码(可能是一个新关键字或类似“nargs”的特殊变量来检索计数),而C通常尽可能地保持极简主义。
...
允许没有参数,例如:int printf(const char *format, ...);
语句。
printf("foobar\n");
是有效的。
如果您不强制要求至少一个参数(应该用于检查更多参数),函数就无法“知道”它是如何被调用的。
所有这些语句都是有效的。
f();
f(1, 2, 3, 4, 5);
f("foobar\n");
f(qsort);