C中的整数和浮点除法

时间:2019-01-28 01:26:36

标签: c

谁能告诉我为什么会这样... 我使用最新的GCC编译器对其进行了编译和执行。

请不要给出诸如“用%f代替%d”之类的建议。

#include <stdio.h>

int main(void)
{
    printf("%d" , 3.0 / 2.0);
    return 0;
}

预期输出:1

给出输出:0

#include <stdio.h>

int main(void)
{
    printf("%f" , 3 / 2);
    return 0;
}

预期产量:1.000000

给出的输出:0.000000

2 个答案:

答案 0 :(得分:1)

在第一种情况下,如果格式说明符期望使用double,则传递类型为int的表达式;在第二种情况下,当格式说明符为int时,传递类型的表达式指定者需要一个double

使用错误的格式说明符会调用C标准所规定的undefined behavior,这意味着您无法可靠地预测程序的行为。不同的编译器可能以不同的方式表现出未定义的行为,以及同一编译器上的不同的优化设置。

试图推理未定义的行为没有任何目的。只需确保您的程序运行良好,在这种情况下,这意味着使用正确的格式说明符。

也就是说,x86系统的编译器通常在浮点寄存器中传递浮点参数,而整数则在堆栈上传递。因此,printf尝试读取给定类型的参数时,由于实际的参数根本不存在,最终它会读取所查找的任何垃圾。

答案 1 :(得分:1)

计算平台-一起工作的硬件和软件集-定义应如何将参数传递给函数。这种关于如何传递参数的规范是通常称为应用程序二进制接口(ABI)的规范的一部分。

有关应如何传递参数的详细信息可能取决于几个因素,包括:

  • 它的大小,
  • 其对齐要求,
  • 其基本类型(整数,浮点数,指针等),
  • 它是否是某种汇总,例如结构或联合,以及
  • 有关该参数的所有详细信息在编译时是否已知。

传递参数的方式可能包括:

  • 无论是在寄存器中,在堆栈中还是在内存中传递
  • 它如何在内存中对齐,
  • 将其传递给哪一组处理器寄存器。

表达式3 / 2具有int类型。它将以ABI为int参数指定的方式传递。当您以%f格式指定printf时,printf需要一个doubleprintf会在ABI为{{ 1}}。

因此,在double中,无法保证printf("%f" , 3 / 2);甚至看到已传递的printf值。它可能会从错误的内存中获取数据或完全注册。

如果int确实获得了printf值的数据,它将把这些字节解释为int值。字节值在编码double时与在编码int时具有不同的含义。因此,在对double值进行编码时,对int值进行编码的字节中的值不会对1进行编码。因此,即使double在为printf格式化double时,获得%f值为1的字节,它产生的字符也不太可能是“ 1”。

C标准定义了如何使用int及其格式说明符,以便ABI可以工作。当您违反有关将参数类型与格式说明符匹配的C规则时,C标准不会定义行为的结果。它让您受C实现的支配。从历史上看,这意味着您违反了ABI,并且由于我上面描述的原因,该程序将中断。随着时间的流逝,编译器在优化和其他程序转换方面变得更加激进,其结果是,违反C标准规则可以以更令人惊讶的方式转换程序行为。