可变参数函数中的默认参数提升
Default argument promotion in a variadic function
我不清楚以下摘录中使用的短语尾随参数和默认参数提升的含义,其中两个段落看起来几乎是矛盾的,导致我不清楚什么时候应该进行默认促销。
ISO/IEC 9899:201x 部分 6.5.2.2 函数调用:
- 第 6 段:如果表示被调用函数的表达式的类型不包含原型,...,并且类型为
float
的参数被提升为 double
。这些称为 默认参数提升。
- para 7: "...函数原型声明符中的省略号导致参数类型转换在最后一个声明的参数之后停止。默认参数提升是在尾随参数上执行的。"
从第 6 段开始,(不包含原型的类型)似乎表明只有参数 4.0
和 5.0
会进行默认提升.然后在段落中。 7 它表示升级在最后一个声明的参数之后停止。 (我相信是b
)。似乎暗示 a
和 b
将进行提升,但参数列表中跟随它们的任何内容都不会被提升。但随后它继续说默认促销是在尾随参数上执行的。 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
。由于 a
和 b
已经是 float
,因此不需要转换。
其余参数进行默认参数提升(如第 6 段所述),因为它们对应于原型中的省略号。文字 4.0
和 5.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
的调用中没有参数类型转换或提升。
- 参数
a
和b
都是float
类型,与函数的原型和定义相同,所以不需要转换。
- 参数
4.0
和 5.0
都是 double
类型并且不进行默认参数提升。这些值在最后一个声明的参数之后传递,因此它们符合默认参数提升的条件,但由于类型已经是 double
,因此不需要提升。
- 如果参数是
4.0F
而不是 4.0
,则 float
值将默认参数提升为 double
。
如果调用写成:
int res = f(4.0, 5.0, a, b);
然后:
double
参数 4.0
和 5.0
将“隐式转换,就像通过赋值一样”到 float
因为这是原型所需要的。
float
参数 a
和 b
将被提升为类型 double
因为它们进行默认参数提升。
我不清楚以下摘录中使用的短语尾随参数和默认参数提升的含义,其中两个段落看起来几乎是矛盾的,导致我不清楚什么时候应该进行默认促销。
ISO/IEC 9899:201x 部分 6.5.2.2 函数调用:
- 第 6 段:如果表示被调用函数的表达式的类型不包含原型,...,并且类型为
float
的参数被提升为double
。这些称为 默认参数提升。 - para 7: "...函数原型声明符中的省略号导致参数类型转换在最后一个声明的参数之后停止。默认参数提升是在尾随参数上执行的。"
从第 6 段开始,(不包含原型的类型)似乎表明只有参数 4.0
和 5.0
会进行默认提升.然后在段落中。 7 它表示升级在最后一个声明的参数之后停止。 (我相信是b
)。似乎暗示 a
和 b
将进行提升,但参数列表中跟随它们的任何内容都不会被提升。但随后它继续说默认促销是在尾随参数上执行的。 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, ...)
{
...
}
None 条件适用于您的示例。没有原型的函数是指使用古老的 K&R 语法声明的函数:
int f();
当您调用具有此类声明的函数时,所有参数都会进行默认提升。
第 6 段还描述了其他情况,其中存在原型并且调用中的类型与原型中的类型不兼容,但您的类型兼容(它们相同)。
第7段说对前两个参数进行参数转换;它们被转换为原型中指定的 float
。由于 a
和 b
已经是 float
,因此不需要转换。
其余参数进行默认参数提升(如第 6 段所述),因为它们对应于原型中的省略号。文字 4.0
和 5.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 todouble
. 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
的调用中没有参数类型转换或提升。
- 参数
a
和b
都是float
类型,与函数的原型和定义相同,所以不需要转换。 - 参数
4.0
和5.0
都是double
类型并且不进行默认参数提升。这些值在最后一个声明的参数之后传递,因此它们符合默认参数提升的条件,但由于类型已经是double
,因此不需要提升。 - 如果参数是
4.0F
而不是4.0
,则float
值将默认参数提升为double
。
如果调用写成:
int res = f(4.0, 5.0, a, b);
然后:
double
参数4.0
和5.0
将“隐式转换,就像通过赋值一样”到float
因为这是原型所需要的。float
参数a
和b
将被提升为类型double
因为它们进行默认参数提升。