最后命名参数不是函数还是数组?

Joh*_*itb 16 c c++ arrays variadic-functions

这个问题是关于vararg函数,以及省略号之前的最后一个命名参数:

void f(Type paramN, ...) {
  va_list ap;
  va_start(ap, paramN);
  va_end(ap);
}
Run Code Online (Sandbox Code Playgroud)

我正在阅读C标准,并发现va_start宏的以下限制:

参数parmN是函数定义中变量参数列表中最右边参数的标识符(恰好在......之前).如果参数parmN使用寄存器存储类,函数或数组类型声明,或者使用与应用默认参数提升后生成的类型不兼容的类型,则行为未定义.

我想知道为什么以下代码的行为未定义

void f(int paramN[], ...) {
  va_list ap;
  va_start(ap, paramN);
  va_end(ap);
}
Run Code Online (Sandbox Code Playgroud)

并且未定义以下内容

void f(int *paramN, ...) {
  va_list ap;
  va_start(ap, paramN);
  va_end(ap);
}
Run Code Online (Sandbox Code Playgroud)

宏可以通过纯C代码实现.但纯C代码无法确定是否paramN声明为数组或指针.在这两种情况下,参数的类型都被调整为指针.功能类型参数也是如此.

我想知道:这个限制的理由是什么?在内部进行这些参数调整时,某些编译器是否存在实现此问题的问题?(C++也说明了相同的未定义行为 - 所以我的问题是关于C++的问题).

Mic*_*urr 6

对寄存器参数或函数参数的限制可能类似于:

  • 您不能使用register存储类获取变量的地址.
  • 函数指针有时与指向对象的指针完全不同.例如,它们可能比指向对象的指针大(您无法将函数指针可靠地转换为对象指针并再次返回),因此向函数指针的地址添加一些固定数字可能无法使您进入下一个参数.如果va_start()和/或va_arg()通过向地址添加一些固定数量来实现,paramN并且函数指针大于对象指针,则计算最终会va_arg()返回对象返回的错误地址.这似乎不是实现这些宏的好方法,但可能存在具有(甚至需要)此类实现的平台.

我想不出阻止允许数组参数会出现什么问题,但是PJ Plauger在他的书"标准C库"中这样说:

对定义的宏施加的一些限制<stdarg.h>似乎不必要地严重.对于某些实现,它们是.然而,每个都被引入,以满足至少一个严重的C实现的需要.

我想,很少有人比Plauger更了解C库的来龙去脉.我希望有人可以用一个实际的例子回答这个具体问题; 我认为这将是一个有趣的琐事.

新信息:


"国际标准的基本原理 - 编程语言 - C"说va_start():

parmN参数va_start旨在帮助实现者va_start完全用C语言编写一致的宏的定义,甚至使用前C89编译器(例如,通过获取参数的地址).对parmN参数声明的限制遵循允许这种实现的意图,因为如果参数的声明不符合这些限制,将&运算符应用于参数名称可能不会产生预期的结果.

这并不能帮助我限制数组参数.


Pav*_*aev 3

这不是未定义的。请记住,当参数声明为 时int paramN[],实际参数类型仍将衰减为int* paramN立即(这在 C++ 中可见,例如,如果您应用于typeidparamN

我必须承认,考虑到您首先不能拥有函数或数组类型的参数(因为它们会指针衰减),我什至不确定规范中的这一点的用途。

  • 文本显示“如果使用 [...] 函数或数组类型 [...] 声明参数 parmN,则行为未定义。”。如果我没记错的话,我认为这适用于包含“int paramN[]”的倒数第二个片段。另外“man va_start”说:“因为这个参数的地址可能在 va_start() 宏中使用,所以它不应该被声明为寄存器变量,或者函数或数组类型。”。诡异的 (3认同)