大家好,
int main()
{
int a = 2;
double b=2;
printf( "with the d a = %d \n", a); // print 2
printf( "with the d a = %d \n", b); // **print 0 <== My interrogation?**
printf( "with the f a = %f", b); // print 2.000000
return 0;
}
我知道,由于我们将b声明为双精度型,因此必须在printf函数中使用%f。 我的问题是:在内存深处,为什么我们将0打印出来,因为我们在b中放入了一个int(即使程序已经为double保留了内存)?
谢谢
答案 0 :(得分:4)
2
作为IEEE 754的double形式的二进制表示形式为0x4000000000000000
,将double
传递给期望int
的printf可能导致强制转换为整数,并且仅查看全部为0
的底部4个字节,因此将打印0
。
所有这些都是未定义的行为,因此不应依赖于此,并且需要确保使用正确的格式字符串。如果您的格式字符串与参数不匹配,一些现代编译器甚至会发出警告。
答案 1 :(得分:1)
您的行为不确定。
您要C来查看您的double所占用的内存,并将其解释为int。
可能正在发生的事情是将double加载到浮点寄存器中,而printf在堆栈中寻找int值。
这几天,当格式字符串与参数类型不匹配时,任何不错的编译器都应向您发出警告。务必阅读这些警告!
答案 2 :(得分:0)
这是未定义的行为(UB),因为格式字符串和参数之间不匹配。话虽如此,并假设编译器没有进行一些非平凡的转换,那么在这种情况下,UB具有一些典型的行为。
要查看会发生什么,您必须检查系统上的调用约定。该程序最有可能在64位x86体系结构上运行,该体系结构根据X86 calling conventions传递参数。在Windows上,第一个浮点值通过与其他整数参数(RCX,RDX,R8,R9中的一个)不同的寄存器(XMM0)传递。这意味着:
printf( "with the d a = %d \n", b);
double b
传递到一个位置,而"%d"
从另一个位置读取。另请注意,相关的整数被认为是易失性的,并且可以通过更早调用printf
来丢弃。这意味着通过寄存器传递到前一个printf
的int(a = 2)被相同的printf
破坏。实际上,“%d”使printf
读取从printf
的内部实现中遗留下来的垃圾值。
结果,在Linux和Mac上,使用clang和gcc时,我得到的是随机垃圾值,而不是零。我假设最初的问题是在Windows上引用Visual Studio,这可能会在调试模式(或类似方式)下将整数设置为零。
请注意,无法保证行为未定义。编译器可能会做任何事情,包括删除有问题的行,或者做完全出乎意料的事情。对于给定的编译器,系统和优化级别,此答案的分析是正确的。在生成的程序集中观察到此错误,但是任何细微的更改(甚至是代码中的其他功能)都可能导致完全不同的行为。永远不要依赖未定义的行为。