C:在函数调用上传递参数时的类型转换

来自C编程语言第2版:

由于函数调用的参数是表达式,因此在将参数传递给函数时也会发生类型转换。 在没有函数原型的情况下,char和short变为int,float变为double。

通过阅读文本,我得到的印象是,除非您通过使用强制转换或函数原型显式指定参数类型,否则函数参数将始终作为int或double传递。

为了validation我的假设,我编译了以下代码:

#include  main() { unsigned char c = 'Z'; float number = 3.14f; function_call(c, number); } void function_call(char c, float f) { } 

编译后我得到以下警告:

typeconversion.c:11:警告:’function_call’的冲突类型

typeconversion.c:7:警告:’function_call’之前的隐式声明就在这里

我的猜测是c和数字都在函数调用中转换为int和double,然后转换回char和float。 这是真的发生了什么?

演员阵容无关紧要,重要的是(可能隐含的)原型。

 void foo(short s) { // do something } int main(void) { signed char c = 'a'; foo(c); // c is promoted to short by explicit prototype bar(c); // c is promoted to int by implicit prototype } void bar(int i) { // do something } 

当书中写着“函数调用的参数是表达式”时,它意味着适用相同类型的促销规则。 如果将函数参数视为对函数原型中指定的变量的隐式赋值,则可能更容易理解。 例如,在上面的foo()调用中,有一个隐含的short s = c

这就是演员无关紧要的原因。 请考虑以下代码段:

 signed char c = 'a'; int i = (short) c; 

这里c的值首先提升为short (显式)然后提升为int (隐式)。 i的值将始终为int

至于charshort成为intfloat变为double ,引用隐式函数原型的默认类型。 当编译器在看到函数的原型或函数定义之前看到对函数的调用时,它会自动生成原型。 对于整数值,它默认为int ,对于浮点值,它默认为double

如果最终的函数声明与隐式原型不匹配,您将收到警告。

你已经大致了解了什么是错的,但并不完全正确。

你写的时候发生了什么事

 function_call(c, number); 

编译器看到你正在调用一个它尚未见过的函数,因此必须决定它的签名应该是什么。 根据您之前引用的促销规则,它将char提升为int并将float提升为double并确定签名为

 function_call(int, double) 

然后当它看到

 function_call(char c, float f) 

它将此解释为具有相同名称的不同函数的签名,这在C中是不允许的。它与您实际定义函数的原型不同的错误完全相同,只是在这种情况下原型是隐式的由编译器生成。

因此,正是这个规则引起了问题,但错误与实际在类型之间来回转换值没有任何关系。

编译器抱怨假设function_call是一个int返回函数,如标准所示,然后你告诉它是一个void函数。 除非您明确声明它们与实际函数不同,否则编译器不会关心参数。 你可以传递它没有参数,它不会抱怨。

您必须始终声明您的函数,因为如果函数在其他模块中,则此错误将不会被检测到。 如果函数应该返回任何可能大于int的类型,例如void *或long,那么调用函数中的int转换为int很可能会截断它,给你留下一个奇怪的bug。

每个人都错过了一件事。 在ISO C中,ISO语法原型会覆盖默认参数提升。

在这种情况下,允许编译器纯粹基于定义的样式生成不同的代码(!) 。 这可以让您获得K&R兼容性,但除非您编写了ISO代码,否则您无法在语言级别之间进行调用,因为K&R期望或修改K&R代码以查看ISO原型。

尝试使用cc -S -O ……

 extern float q; void f(float a) { q = a; } movl 8(%ebp), %eax movl %eax, q extern float q; void f(a) float a; { q = a; } // Not the same thing! fldl 8(%ebp) fstps q