C函数调用中的默认参数促销

build立

在调用C中的函数时,我有几个关于默认参数促销的问题。6.5.2.2“函数调用” C99标准中的第6,7和8段(pdf) (为了方便起见,强调增加并分成列表读):

第6段

  1. 如果表示被调用函数的expression式的types不包含原型 ,则将对每个参数执行整数提升,将types为float参数提升为double 。 这些被称为默认参数促销
  2. 如果参数个数不等于参数个数,则行为是不确定的。
  3. 如果使用包含原型的types定义函数,并且原型以省略号( , ... )结尾,或者升级后的参数types与参数types不兼容,则行为是不确定的。
  4. 如果使用不包含原型的types定义函数,并且升级之后的参数types与升级之后的参数types不兼容,则行为是未定义的,但以下情况除外:
    • 一个提升types是一个有符号的整数types,另一个提升types是对应的无符号整数types,并且该值可以在两种types中表示;
    • 这两种types都是指向合格或不合格版本的字符types或void指针。

第7段

  1. 如果表示被调用函数的expression式包含一个包含原型的types, 那么这些参数就像通过赋值一样被隐式转换为相应参数的types,并将每个参数的types作为其声明的非限定版本types。
  2. 函数原型声明符中的省略号表示法导致参数types转换在最后声明的参数后停止。 在结尾参数上执行默认参数促销。

第8段

  1. 没有其他转换隐式执行; 特别是参数的数量和types不能与函数定义中不包含函数原型声明的参数进行比较。

我知道的

  • 默认的参数促销charint / unsigned intfloatdouble short
  • variadic函数的可选参数(如printf )受默认参数促销的限制

为了logging,我对函数原型的理解是这样的:

 void func(int a, char b, float c); // Function prototype void func(int a, char b, float c) { /* ... */ } // Function definition 

所有这些我都很难过。 以下是我的一些问题:

  • 原型和非原型function的行为真的有很大不同,比如默认的促销和隐式转换?
  • 默认参数促销何时发生? 它总是? 还是只是在特殊情况下(如可变参数函数)? 这取决于一个函数是否是原型的?

Upvoted AProgrammer的答案 – 那些是真正的货物。

对于那些想知道为什么事情是这样的人的:在1988年以前的黑暗时代,在经典的“K&R”C中没有这样的function原型,而默认的参数促销是由于(a)基本上“免费”,因为不需要在寄存器中放置一个字节,而是在寄存器中放置一个字,并且(b)减lessparameter passing中的潜在错误。 第二个原因从来没有削减过,所以在ANSI C中引入函数原型是C语言中唯一最重要的变化。

至于什么时候默认的促销活动开始时:当参数的期望types是未知的也就是说当没有原型或参数是可变的时, 默认的参数促销就被使用了

  • (非可变参数)函数与原型转换为相应的types,可以是char,short,float。

  • 没有原型和可变参数的函数的参数受默认参数促销的限制。

如果你用一个原型定义了一个函数,并且在没有原型的情况下使用它,反之亦然,并且它有char,short或者floattypes的参数,那么在运行时你可能会遇到问题。 如果升级的types与读取参数列表时使用的types不匹配,您将会遇到与可变参数函数相同的问题。

示例1:在定义具有原型的函数时使用该函数时出现问题。

definition.c

 void f(char c) { printf("%c", c); } 

use.c

 void f(); int main() { f('x'); } 

可以失败,因为一个int将被传递和函数期望一个字符。

示例2:定义没有原型的函数时使用它的问题。

definition.c

 void f(c) char c; { printf("%c", c); } 

(这种定义非常老套)

use.c

 void f(char c); int main() { f('x'); } 

可能会失败,因为一个int是预期的,但一个字符将被传递。

注意:你会注意到标准库中的所有函数都具有默认促销的types。 因此,在添加原型时,它们在转换过程中不会造成问题。

你的困惑源于对术语的一个很小的误解 – 声明和定义都可以包括原型(或不包括):

 void func(int a, char b, float c); 

这是一个包含原型的函数声明

 void func(int a, char b, float c) { /* ... */ } 

这是一个包含原型的函数定义

“Prototyped”和“non-prototyped”只是一个函数types的属性,声明和定义都引入了函数的types。

所以你可以在没有原型的情况下进行声明:

 void func(); 

或者你可以有一个没有原型的定义(K&R C风格):

 void func(a, b, c) int a; char b; float c; { /* ... */ }