Spa*_*ato 5 c int printf casting
在C中,当我移动此printf行时:printf("%f\n", 5 / 2);对于不同的行,其输出会发生变化.有任何想法吗?
下面是代码:
#include <stdlib.h>
#include <stdio.h>
int main()
{
int a = 65;
char c = (char)a;
int m = 3.0/2;
printf("%c\n", c);
printf("%f\n", (float)a);
printf("%f\n", 5.0 / 2);
printf("%f\n", 5 / 2.0);
printf("%f\n", (float)5 / 2);
printf("%f\n", 5 / (float)2);
printf("%f\n", (float)(5 / 2));
printf("%f\n", 5.0 / 2);
printf("%d\n", m);
printf("%f\n", 5 / 2);
system("PAUSE");
return(0);
}
Run Code Online (Sandbox Code Playgroud)
继承人的产量:
A
65.000000
2.500000
2.500000
2.500000
2.500000
2.000000
2.500000
1
2.500000
Run Code Online (Sandbox Code Playgroud)
如果我移动printf("%f\n", 5 / 2);到第一行之一(在输出A的那一行和输出65.000000的那一行之间),它将打印0.000000(这是有意义的)而不是现在的2.500000.有任何想法吗?
小智 3
正如评论者所指出的,这条线printf("%f\n", 5 / 2);只是表现出未定义的行为。但让我们看看为什么您可能会在使用 System V ABI 的 x86-64 架构上得到这样的结果。
简而言之,前几个参数是通过寄存器进行通信的。选择取决于参数的类型:整数参数进入“经典”寄存器(edi、esi等),浮点参数进入 SSE 寄存器(xmm0、xmm1等)。
因为我们在格式字符串中给出了错误的类型,printf所以从错误的寄存器中读取参数。
让我们将您的程序简化为以下内容:
#include <stdio.h>
int main(void)
{
printf("%f\n", 5/2);
printf("%f\n", 5.0/2);
printf("%f\n", 5/2);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
现在让我们来看看 的反汇编main。我们从函数序言开始,它并不太特别:
push %rbp
mov %rsp,%rbp
sub $0x10,%rsp
Run Code Online (Sandbox Code Playgroud)
然后,我们第一次调用printf,参数被传递到edi(它获取指向格式字符串的指针)和esi( 5/2,这是2由于整数除法):
mov $0x2,%esi
mov $0x4005e4,%edi
mov $0x0,%eax
callq 4003e0 <printf@plt>
Run Code Online (Sandbox Code Playgroud)
但是,printf将读取"%f\n"格式并尝试从 中读取参数xmm0。就我而言,该寄存器的值为0,因此打印出0.000000。
在第二次调用中,参数显然是一个浮点数,它通过以下方式传递xmm0:
movabs $0x4004000000000000,%rax
mov %rax,-0x8(%rbp)
movsd -0x8(%rbp),%xmm0
mov $0x4005e4,%edi
mov $0x1,%eax
callq 4003e0 <printf@plt>
Run Code Online (Sandbox Code Playgroud)
现在,printf打印出预期的结果2.500000(您在此处看到的是0x4004000000000000,这就是 2.5 的 64 位浮点常量的样子)。我们传递它xmm0,它从 读取它xmm0。
第三次调用与第一个调用完全相同:
mov $0x2,%esi
mov $0x4005e4,%edi
mov $0x0,%eax
callq 4003e0 <printf@plt>
Run Code Online (Sandbox Code Playgroud)
发生的变化是对 的调用printf没有改变 中的值xmm0。当我们第三次调用时,它仍然包含第二次调用之前的常量 2.5 printf。在第三次调用时,printf将2.500000再次打印。
(当然,我们的函数以无聊的结束return 0:)
mov $0x0,%eax
leaveq
retq
Run Code Online (Sandbox Code Playgroud)