如果一个函数是K&R样式定义的,那么参数类型转换会发生什么?

3
即使函数是以K&R风格定义的,我仍然希望参数进行类型转换。然而,测试程序的输出如下:
int 9.42
int 2.97005e-12
flt 9.42
flt 9.42

看起来 int 值没有转换为 double。为什么?

#include <stdio.h>

double mult_pi_usual(double v) {
  return v * 3.14;
}

double mult_pi_KR(v)
double v;
{
  return v * 3.14;
}

int main() {
  printf("int %g\n", mult_pi_usual(3));
  printf("int %g\n", mult_pi_KR(3));
  printf("flt %g\n", mult_pi_usual(3.));
  printf("flt %g\n", mult_pi_KR(3.));
}

哪个编译器? - stark
i586-alt-linux-gcc(GCC)5.3.1 20151207(ALT Linux 5.3.1-alt3) - olpa
值得一提的是,在 Fedora 25 上使用 gcc 6.2.1 也会得到相同的结果。这两个函数具有相同的汇编表示,但正如 OP 所述,multi_pi_KR() 的参数并不作为 double 传递。它在 EDI 寄存器中传递,并且 EAX/rax 被设置为 0(即没有 FP 参数)。 - Bjorn A.
2个回答

1

从C89/C90标准(6.3.2.2):

如果函数的定义不包括原型类型,并且在提升后的参数类型与提升后的参数类型不兼容,则行为未定义。

当你将值3传递给mult_pi_KR(3)中的double参数时,会调用未定义的行为,一些方式下存储在v中的值变为0.000000。这个值无法确定,因为它是一个未定义的行为。由于在浮点表示中永远不能精确地存储0,所以将其乘以3.14得到奇怪的值,如2.97005e-12

我尝试了相同的示例,但将v的类型更改为intmult_pi_KR中。然后,第一次调用mult_pi_KR正常工作,但由于第二次调用中的类型不匹配,值1被存储在v中。再次出现由于未定义行为而导致的值。

1
作为一种声明,K&R风格不会传达类型信息以在调用点进行检查。
考虑这样一个事实,即K&R函数声明(例如在头文件中)可能是:
double mult_pi_KR();

double v仅在函数定义内可见。

为了保持编译单元的一致性(并非所有编译单元都能看到完整的函数定义),调用函数时不会检查参数的类型。

在您的示例中,这意味着编译器 - 在调用CALLED函数的点上 - 不知道mult_pi_KR()接受一个double。因此,mult_pi_KR(3)不会将3提升为double

int传递给期望double的函数的结果实际上是未定义的行为。


所以,编译器故意忽略了类型注释。这个解释是合理的,但是我的思维对此反感。这种行为在规范中有描述吗? - olpa
您IP地址为143.198.54.68,由于运营成本限制,当前对于免费用户的使用频率限制为每个IP每72小时10次对话,如需解除限制,请点击左下角设置图标按钮(手机用户先点击左上角菜单按钮)。 - Peter
使用不在函数定义中的信息意味着如果调用函数被移动到另一个文件,编译器将生成不同的代码。 - stark
@stark - 对的,不过你需要使用“声明”一词,而不是“定义”。[这有点模糊,因为在正式上,函数定义是函数声明的一种类型,但并非所有的函数声明都是定义。所谓的函数“原型”是一个声明,但不是定义。] - Peter

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接