可变参数函数中的默认参数提升

Default argument promotion in a variadic function

我不清楚以下摘录中使用的短语尾随参数默认参数提升的含义,其中两个段落看起来几乎是矛盾的,导致我不清楚什么时候应该进行默认促销。

ISO/IEC 9899:201x 部分 6.5.2.2 函数调用:

从第 6 段开始,(不包含原型的类型)似乎表明只有参数 4.05.0 会进行默认提升.然后在段落中。 7 它表示升级在最后一个声明的参数之后停止。 (我相信是b)。似乎暗示 ab 将进行提升,但参数列表中跟随它们的任何内容都不会被提升。但随后它继续说默认促销是在尾随参数上执行的。 Trailing表示的末尾,表示省略号允许的。

那么调用 f() 时究竟提升了什么,为什么?

 int f(float a, float b, ...);
 
 int main(void)
 {
     float a = 1.0;
     float b = 2.0;

     int res = f(a, b, 4.0, 5.0);
     return 0;
 }

 int f(float a, float b, ...)
 {
     ...
 }
第 6 段中的

None 条件适用于您的示例。没有原型的函数是指使用古老的 K&R 语法声明的函数:

int f();

当您调用具有此类声明的函数时,所有参数都会进行默认提升。

第 6 段还描述了其他情况,其中存在原型并且调用中的类型与原型中的类型不兼容,但您的类型兼容(它们相同)。

第7段说对前两个参数进行参数转换;它们被转换为原型中指定的 float。由于 ab 已经是 float,因此不需要转换。

其余参数进行默认参数提升(如第 6 段所述),因为它们对应于原型中的省略号。文字 4.05.0 的类型为 double,因此无需提升。

根据 C11 标准 §6.5.2.2 Function calls(我认为这在 C18 中没有改变),第 6 段和第 7 段的完整引用是:

¶6 If the expression that denotes the called function has a type that does not include a prototype, the integer promotions are performed on each argument, and arguments that have type float are promoted to double. These are called the default argument promotions. If the number of arguments does not equal the number of parameters, the behavior is undefined. If the function is defined with a type that includes a prototype, and either the prototype ends with an ellipsis (, ...) or the types of the arguments after promotion are not compatible with the types of the parameters, the behavior is undefined. If the function is defined with a type that does not include a prototype, and the types of the arguments after promotion are not compatible with those of the parameters after promotion, the behavior is undefined, except for the following cases:

  • one promoted type is a signed integer type, the other promoted type is the corresponding unsigned integer type, and the value is representable in both types;
  • both types are pointers to qualified or unqualified versions of a character type or void.

¶7 If the expression that denotes the called function has a type that does include a prototype, the arguments are implicitly converted, as if by assignment, to the types of the corresponding parameters, taking the type of each parameter to be the unqualified version of its declared type. The ellipsis notation in a function prototype declarator causes argument type conversion to stop after the last declared parameter. The default argument promotions are performed on trailing arguments.

给出问题的代码:

int f(float a, float b, ...);
 
int main(void)
{
    float a = 1.0;
    float b = 2.0;

    int res = f(a, b, 4.0, 5.0);
    return 0;
}

int f(float a, float b, ...)
{
     ...
}

在对 f 的调用中没有参数类型转换或提升。

  • 参数ab都是float类型,与函数的原型和定义相同,所以不需要转换。
  • 参数 4.05.0 都是 double 类型并且不进行默认参数提升。这些值在最后一个声明的参数之后传递,因此它们符合默认参数提升的条件,但由于类型已经是 double,因此不需要提升。
  • 如果参数是 4.0F 而不是 4.0,则 float 值将默认参数提升为 double

如果调用写成:

int res = f(4.0, 5.0, a, b);

然后:

  • double 参数 4.05.0 将“隐式转换,就像通过赋值一样”到 float 因为这是原型所需要的。
  • float 参数 ab 将被提升为类型 double 因为它们进行默认参数提升。