谁能告诉我为什么会这样... 我使用最新的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
答案 0 :(得分:1)
在第一种情况下,如果格式说明符期望使用double
,则传递类型为int
的表达式;在第二种情况下,当格式说明符为int
时,传递类型的表达式指定者需要一个double
。
使用错误的格式说明符会调用C标准所规定的undefined behavior,这意味着您无法可靠地预测程序的行为。不同的编译器可能以不同的方式表现出未定义的行为,以及同一编译器上的不同的优化设置。
试图推理未定义的行为没有任何目的。只需确保您的程序运行良好,在这种情况下,这意味着使用正确的格式说明符。
也就是说,x86系统的编译器通常在浮点寄存器中传递浮点参数,而整数则在堆栈上传递。因此,printf
尝试读取给定类型的参数时,由于实际的参数根本不存在,最终它会读取所查找的任何垃圾。
答案 1 :(得分:1)
计算平台-一起工作的硬件和软件集-定义应如何将参数传递给函数。这种关于如何传递参数的规范是通常称为应用程序二进制接口(ABI)的规范的一部分。
有关应如何传递参数的详细信息可能取决于几个因素,包括:
传递参数的方式可能包括:
表达式3 / 2
具有int
类型。它将以ABI为int
参数指定的方式传递。当您以%f
格式指定printf
时,printf
需要一个double
,printf
会在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标准规则可以以更令人惊讶的方式转换程序行为。