为什么 C 中不允许 void f(...) ?

发布于 2024-11-18 12:01:29 字数 98 浏览 5 评论 0原文

为什么 C 不允许具有可变长度参数列表的函数,例如:

void f(...)
{
    // do something...
}

Why doesn't C allow a function with variable length argument list such as:

void f(...)
{
    // do something...
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

傲鸠 2024-11-25 12:01:29

我认为要求 varargs 函数必须具有命名参数的动机是为了 va_start 的统一。为了便于实现,va_start 采用最后一个命名参数的名称。使用典型的可变参数调用约定,并且根据参数存储的方向,va_arg 将在地址 (¶meter_name) + 1( first_vararg_type*)(¶meter_name) - 1,加上或减去一些填充以确保对齐。

我不认为该语言有什么特殊原因不能支持没有命名参数的可变参数函数。在此类函数中,必须有一种 va_start 的替代形式,它必须直接从堆栈指针获取第一个 vararg(或者是迂腐的帧指针,这实际上是堆栈指针在函数入口处的值,因为函数中的代码很可能在函数入口后移动了 sp)。原则上这是可能的——任何实现都应该在某种程度上以某种方式访问​​堆栈[*]——但这对于某些实现者来说可能很烦人。一旦了解了 varargs 调用约定,您通常就可以实现 va_ 宏,而无需任何其他特定于实现的知识,这还需要知道如何直接获取调用参数。我之前已经在模拟层中实现了这些可变参数宏,这会让我很恼火。

此外,没有命名参数的 varargs 函数没有太多实际用途。可变参数函数没有语言功能来确定变量参数的类型和数量,因此被调用者无论如何都必须知道第一个可变参数的类型才能读取它。因此,您不妨将其设为具有类型的命名参数。在 printf 及其朋友中,第一个参数的告诉函数可变参数的类型是什么,以及有多少个。

我认为理论上,被调用者可以查看一些全局变量来弄清楚如何读取第一个参数(以及是否有),但这非常令人讨厌。我当然不会竭尽全力支持这一点,并且添加带有额外实现负担的新版本 va_start 是不符合我的方式。

[*] 或者如果实现不使用堆栈,则使用它所使用的任何内容来传递函数参数。

I think the motivation for the requirement that varargs functions must have a named parameter is for uniformity of va_start. For ease of implementation, va_start takes the name of the last named parameter. With a typical varargs calling convention, and depending on the direction arguments are stored, va_arg will find the first vararg at address (¶meter_name) + 1 or (first_vararg_type*)(¶meter_name) - 1, plus or minus some padding to ensure alignment.

I don't think there's any particular reason why the language couldn't support varargs functions with no named parameters. There would have to be an alternative form of va_start for use in such functions, that would have to get the first vararg directly from the stack pointer (or to be pedantic the frame pointer, which is in effect the value that the stack pointer had on function entry, since the code in the function might well have moved the sp since function entry). That's possible in principle -- any implementation should have access to the stack[*] somehow, at some level -- but it might be annoying for some implementers. Once you know the varargs calling convention you can generally implement the va_ macros without any other implementation-specific knowledge, and this would require also knowing how to get at the call arguments directly. I have implemented those varargs macros before, in an emulation layer, and it would have annoyed me.

Also, there's not a lot of practical use for a varargs function with no named parameters. There's no language feature for a varargs function to determine the type and number of variable arguments, so the callee has to know the type of the first vararg anyway in order to read it. So you might as well make it a named parameter with a type. In printf and friends the value of the first parameter tells the function what the types are of the varargs, and how many of them there are.

I suppose that in theory the callee could look at some global to figure out how to read the first argument (and whether there even is one), but that's pretty nasty. I would certainly not go out of my way to support that, and adding a new version of va_start with extra implementation burden is going out of my way.

[*] or if the implementation doesn't use a stack, to whatever it uses instead to pass function arguments.

冷血 2024-11-25 12:01:29

对于可变长度参数列表,您必须声明第一个参数的类型 - 这是该语言的语法。

void f(int k, ...)
{
    /* do something */
}

会工作得很好。然后,您必须在函数内使用 va_listva_startva_end 等来访问各个参数。

With variable-length argument list you must declare the type of the first argument - that's the syntax of the language.

void f(int k, ...)
{
    /* do something */
}

will work just fine. You then have to use va_list, va_start, va_end, etc. inside the function to access individual arguments.

缱绻入梦 2024-11-25 12:01:29

C 确实允许可变长度参数,但您需要使用 va_list、va_start、va_end 等. 对于它。您认为 printf 和朋友是如何实现的?也就是说,我建议不要这样做。您通常可以使用数组或结构作为参数更干净地完成类似的事情。

C does allow for variable length arguments, but you need to use va_list, va_start, va_end, etc. for it. How do you think printf and friends are implemented? That said, I would recommend against it. You can usually accomplish a similar thing more cleanly using an array or struct for the parameters.

杀手六號 2024-11-25 12:01:29

尝试了一下,做出了这个很好的实现,我认为有些人可能会考虑。

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);
}

它确保您最少有一个变量,但允许您以一种干净的方式将它们全部放入循环中。

Playing around with it, made this nice implementation that I think some people might want to consider.

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);
}

It ensures you have one variable minimum, but allows you to put them all in a loop in a clean way.

甜点 2024-11-25 12:01:29

C 没有什么内在原因不能接受 void f(...)。可以,但这个 C 功能的“设计者”决定不这样做。

我对他们动机的猜测是,允许 void f(...) 需要比不允许它更多的“隐藏”代码(可以被视为运行时):为了区分 f() 和 f(arg )(以及其他),C 应该提供一种方法来计算给定的参数数量,这需要更多生成的代码(可能需要一个新关键字或特殊变量,例如“nargs”来检索计数),并且 C 通常尝试尽可能简约。

There's no an intrisic reason why C can't accept void f(...). It could, but "designers" of this C feature decided not to do so.

My speculation about their motivations is that allowing void f(...) would require more "hidden" code (that can be accounted as a runtime) than not allowing it: in order to make distinguishable the case f() from f(arg) (and the others), C should provide a way to count how many args are given, and this needs more generated code (and likely a new keyword or a special variable like say "nargs" to retrieve the count), and C usually tries to be as minimalist as possible.

归属感 2024-11-25 12:01:29

... 允许不带参数,即:对于 int printf(const char *format, ...); 该语句

printf("foobar\n");

是有效的。

如果您不强制要求至少有 1 个参数(应该用于检查更多参数),则函数无法“知道”它是如何被调用的。

所有这些陈述都是有效的

f();
f(1, 2, 3, 4, 5);
f("foobar\n");
f(qsort);

The ... allows for no arguments, ie: for int printf(const char *format, ...); the statement

printf("foobar\n");

is valid.

If you don't mandate at least 1 parameter (which should be used to check for more parameters), there is no way for the function to "know" how it was called.

All these statements would be valid

f();
f(1, 2, 3, 4, 5);
f("foobar\n");
f(qsort);
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文