我在阅读c++标准时有这个问题,但它基本上只是引用C标准,所以我想这个问题适用于两种语言。
从[cstdarg.syn]
如果形参parmN是引用类型,或者是与传递没有形参的实参时产生的类型不兼容的类型,则行为是未定义的。
我不明白"兼容"的规则。类型。兼容类型在C语言中几乎是相同的类型,那么这个规则到底是什么意思呢?parmN
不能是float
,bool
,char
,short
?
int add_nums(short count, ...)
{
int result = 0;
std::va_list args;
va_start(args, count); // undefined behavior ?
for (short i = 0; i < count; ++i) {
result += va_arg(args, int);
}
va_end(args);
return result;
}
还有,这条规则背后的原因是什么?我明白为什么parmN
不能被引用,但我不明白它的类型是如何与可变参数的类型相关的。
关于类型的规则与参数的提升有关。
对于可变参数,传递类型为float
的参数提升为double
,传递类型小于int
的整型参数提升为int
或unsigned int
。这意味着va_arg
不能期望这些类型的参数,否则会触发未定义的行为。
关于可变函数的这种行为记录在C11标准的6.5.2.2p7节中:
表示被调用函数的表达式的类型为包含原型,则参数隐式转换为如果通过赋值,则为相应形参的类型,取指定每个参数的类型为其非限定版本声明的类型。函数原型中的省略号符号声明器导致参数类型转换在最后一个之后停止声明的参数。执行默认参数提升落后于参数。
和术语默认参数提升在6.5.2.2p6中定义:
表示被调用函数的表达式的类型为不包含一个原型,整数提升执行类型为
float
的参数被提升为double
。这些称为默认参数提升。
看一下:
- c++ -默认转换
- C -默认参数提升
在C参考中有一个很好的例子,说明了(令人困惑的)语句的含义:
int add_nums(int count, ...);
int sum = add_nums(2, 'c', true); // add_nums is called with three ints: (2, 99, 1)
c++的参考很清楚,
当调用可变参数函数时,在左值到右值、数组到指针和函数到指针的转换之后,作为可变参数列表一部分的每个实参都要进行额外的转换,称为默认实参提升:
…
只允许算术、枚举、指针、指向成员的指针和类类型实参(转换后)。然而,……(一些其他的东西)