C:在函数调用中传递参数时的类型转换
从C编程语言第二版: 由于函数调用的参数是表达式,所以在将参数传递给函数时也会发生类型转换。在缺少函数原型的情况下,char和short变为int,float变为double 通过阅读本文,我得到的印象是,除非通过使用cast或函数原型显式指定参数类型,否则函数参数将始终作为int或double传递 为了验证我的假设,我编译了以下代码:C:在函数调用中传递参数时的类型转换,c,type-conversion,C,Type Conversion,从C编程语言第二版: 由于函数调用的参数是表达式,所以在将参数传递给函数时也会发生类型转换。在缺少函数原型的情况下,char和short变为int,float变为double 通过阅读本文,我得到的印象是,除非通过使用cast或函数原型显式指定参数类型,否则函数参数将始终作为int或double传递 为了验证我的假设,我编译了以下代码: #include <stdio.h> main() { unsigned char c = 'Z'; float number
#include <stdio.h>
main()
{
unsigned char c = 'Z';
float number = 3.14f;
function_call(c, number);
}
void function_call(char c, float f)
{
}
#包括
main()
{
无符号字符c='Z';
浮点数=3.14f;
功能调用(c,编号);
}
void函数调用(char c,float f)
{
}
编译后,我收到以下警告:
typeconversion.c:11:警告:“函数调用”的类型冲突
typeconversion.c:7:警告:“function\u call”的上一个隐式声明在这里
我猜c和number在函数调用时都转换为int和double,然后又转换回char和float。这就是实际发生的情况吗?你已经大致了解了问题所在,但并不确切 你写信的时候发生了什么
function_call(c, number);
编译器发现您正在调用一个它还没有看到的函数,因此必须决定它的签名应该是什么。根据前面引用的升级规则,它将char升级为int,float升级为double,并确定签名为
function_call(int, double)
然后当它看到
function_call(char c, float f)
它将此解释为具有相同名称的不同函数的签名,这在C中是不允许的。这完全是相同的错误,就好像您对函数的原型与您实际定义它的方式不同一样,只是在这种情况下,原型是由编译器隐式生成的
因此,是这个规则导致了问题,但是错误与在类型之间来回转换值无关。强制转换是不相关的,重要的是(可能是隐式的)原型
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
}
当书中说“函数调用的参数是表达式”时,这意味着相同类型的提升规则适用。如果将函数参数视为对函数原型中指定的变量的隐式赋值,可能更容易理解。e、 g.在上面对foo()
的调用中,有一个隐式的short s=c
这就是为什么演员不重要。考虑下面的代码片段:
signed char c = 'a';
int i = (short) c;
这里,c的值首先提升为short
(显式),然后提升为int
(隐式)。i
的值始终为int
至于char
和short
变成int
和float
变成double
,这是隐式函数原型的默认类型。当编译器在看到原型或函数定义之前看到对函数的调用时,它会自动生成原型。整数值默认为int
,浮点值默认为double
如果最终的函数声明与隐式原型不匹配,您将收到警告。编译器抱怨它假定函数调用是一个按标准指示的int返回函数,然后您告诉它是一个void函数。编译器不会关心参数,除非您明确声明它们与实际函数不同。你可以传递它,不需要争论,它也不会抱怨
必须始终声明函数,因为如果函数位于其他模块中,则此错误将无法检测到。如果函数返回任何可能大于int的类型,如void*或long,调用函数中转换为int的操作很可能会截断它,给您留下一个奇怪的错误。每个人都错过了一件事。在ISO C中,ISO语法原型覆盖默认参数提升 在这种情况下,编译器可以完全根据定义的样式生成不同的代码(!)。这使您获得了K&R兼容性,但是您不能总是在语言级别之间调用,除非您按照K&R的期望编写了ISO代码,或者修改了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
旧式(K&R)声明以及varargs函数也会出现升级。我认为问题不在于错误本身,尽管这两者肯定是相关的。我看到的唯一问号是附在一个句子后面,询问他对错误原因的解释是否正确。但是,是的,这个问题肯定有几点需要回答。你的帖子回答了我在发布这个问题之前提出的许多问题。这个答案混合了“原型”和“声明”。原型是用来声明函数参数类型的。声明可能包含一个原型,整个过程被称为(“void f();”)。所以你想用“隐式声明”和“显式声明”来代替。这是一个很好的问题,也说明了为什么在范围内始终有一个原型,并且声明了所有参数。“转换回”很重要?为什么?当传递给函数时,
c
和number
的值被强制为类型int
和double
,但没有什么可以“转换回”。编译器错误与他传递的参数无关。你以为是的,你把我骗了。其他答案忽略了这一点,并使OP认为他的编译器将捕获错误的参数错误,而实际情况并非如此。无论什么