C函数调用中的默认参数促销
build立
在调用C中的函数时,我有几个关于默认参数促销的问题。6.5.2.2“函数调用” C99标准中的第6,7和8段(pdf) (为了方便起见,强调增加并分成列表读):
第6段
- 如果表示被调用函数的expression式的types不包含原型 ,则将对每个参数执行整数提升,将types为
float
参数提升为double
。 这些被称为默认参数促销 。- 如果参数个数不等于参数个数,则行为是不确定的。
- 如果使用包含原型的types定义函数,并且原型以省略号(
, ...
)结尾,或者升级后的参数types与参数types不兼容,则行为是不确定的。- 如果使用不包含原型的types定义函数,并且升级之后的参数types与升级之后的参数types不兼容,则行为是未定义的,但以下情况除外:
- 一个提升types是一个有符号的整数types,另一个提升types是对应的无符号整数types,并且该值可以在两种types中表示;
- 这两种types都是指向合格或不合格版本的字符types或
void
指针。
第7段
- 如果表示被调用函数的expression式包含一个包含原型的types, 那么这些参数就像通过赋值一样被隐式转换为相应参数的types,并将每个参数的types作为其声明的非限定版本types。
- 函数原型声明符中的省略号表示法导致参数types转换在最后声明的参数后停止。 在结尾参数上执行默认参数促销。
第8段
- 没有其他转换隐式执行; 特别是参数的数量和types不能与函数定义中不包含函数原型声明的参数进行比较。
我知道的
- 默认的参数促销是
char
和int
/unsigned int
和float
到double
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; { /* ... */ }