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

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

从C编程语言第二版:

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

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

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

#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认为他的编译器将捕获错误的参数错误,而实际情况并非如此。无论什么