为什么C中不允许使用void f(…)?

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

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

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

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

此外,没有命名参数的varargs函数没有很多实际用途。 varargs函数没有语言特性来确定变量参数的类型和数量,因此被调用者必须知道第一个vararg的类型才能读取它。 所以你不妨把它作为一个带有类型的命名参数。 在printf和friends中,第一个参数的告诉函数varargs的类型是什么,以及它们有多少。

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

[*]或者如果实现不使用堆栈,则使用它来代替传递函数参数。

使用可变长度参数列表,您必须声明第一个参数的类型 – 这是语言的语法。

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

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

C允许可变长度参数,但您需要使用va_list,va_start,va_end等 。 您如何看待printf和朋友的实施? 那就是说,我会反对它。 您通常可以使用参数的数组或结构更干净地完成类似的操作。

玩弄它,做了一个很好的实现,我认为有些人可能会考虑。

 template 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(…)没有一个内在的原因。 它可以,但这个Cfunction的“设计师”决定不这样做。

我对他们动机的推测是允许void f(…)需要更多的“隐藏”代码(可以算作运行时)而不是允许它:为了使f()与f(arg)区分开来)(和其他人),C应该提供一种方法来计算给出多少args,这需要更多生成的代码(可能是一个新的关键字或一个特殊的变量,如说“nargs”来检索计数),而C通常尽量做到极简主义。

...允许没有参数,即:for int printf(const char *format, ...); 该声明

 printf("foobar\n"); 

已validation。

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

所有这些陈述都是有效的

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