C 中的默认参数和参数提升

Default argument and parameter promotions in C

我正在研究默认参数提升并在某一点上卡住了。在 C 2011 (ISO/IEC 9899:2011) 中,相关部分似乎是:

§6.5.2.2 Function calls

¶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.

段落的最后三行谈到了在定义时不包含原型的函数类型。

它表示如果提升后的参数类型与提升后的参数类型不兼容,则行为未定义

现在我有一个非常愚蠢的疑问,如果函数声明和函数定义都不包含本段中提到的原型,那么他们在本段的最后三行中谈论的是哪些参数。这里的“parameters after promotion”是什么意思,因为我只研究过参数提升。什么是“参数促销”?

也可以举出上次提到的例外情况的例子。如果有人能用一个合适的例子来解释这一点,那就太好了。

在 C 标准化之前(也就是在 C89 之前),函数的定义不同。为了向后兼容,C11 仍然支持该样式。除非整个目的是玩得开心,否则不要使用它:

int add_ints(); //forward-declaration has no parameters

add_ints(a, b)
//implicit type for return and parameters is int, this only works in pre-standard C or C89/C90
//int a, b; //remove this comment in C99/C11 for it to compile (also add return type int)
{
    return a + b; //side note: old K&R compilers required parantheses around the return expression
}

在某种程度上,这些函数的参数的行为类似于可变参数。调用者不知道函数需要什么参数(与可变参数相同)。它能够向它传递任何参数和任意数量的参数。但是,如果调用语句中的参数数量与声明中的参数数量不匹配,这当然是未定义的行为。

当然,还有一个问题就是由此产生的。如果调用者想要传递一个 short,它如何知道函数期望的是一个 short(并直接传递它)还是一个 int(并需要转换它)?它不能,所以达成了共同点。已决定:

  • charshort 晋升为 int
  • float 晋升为 double

所有以这种方式定义的函数(K&R 样式)和可变参数都会出现这种情况。这样,K&R 函数将永远不会期望 short 参数,因此编译器将始终将 short 参数提升为 int.

当然,正如@aschepler所说,你仍然可以像这样定义函数:

short add_shorts(a, b)
    short a, b;
{
    return a + b;
}

这意味着参数首先转换为 int 并传递给函数,然后函数才将它们转换为 short 并添加它们。

小心 printf():

这样的函数
printf("%.f", 3); //passes an int: UB and also wrong answer (my compiler prints 0)
printf("%.f", 3.0); //correct
printf("%.f", (double)3); //correct

你可能经常看到K&R函数,尤其是作者没有注意在不带参数的函数中添加void关键字:

int f1() //K&R function
{
    return 0;
}
int f2(void) //Standard function
{
    return 0;
}

int main(void) //Don't forget void here as well :P
{
    int a = f1(); //Returns 0
    int b = f2(); //Returns 0
    int c = f1(100); //UB - invalid number of parameters, in practice just returns 0 :)
    int d = f2(100); //Compiler error - parameter number/types don't match

    //A good compiler would give a warning for call #3, but mine doesn't :(
}

编辑:不知道为什么,但是 cppreference 将像 f1() 这样定义的函数分类为它们自己的函数类型(没有 void 的无参数),而不是 K&R 函数。我面前没有标准,但即使标准说的是同样的东西,他们应该表现得一样,他们有我提到的历史。

Default argument promotions

Function declarations in C