Q: 如下代码的输出结果是多少?
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char *argv[])
{
printf("%d\n", 5.0f);
return 0;
}
A: %d本来对应的是整形,实际参数传入浮点数5.0f. 5.0f会强制转换成整形5吗?
对于此类问题, 最佳的分析手法是从原理和汇编出发, 不论什么格式都可以分析出.
首先, printf是如何处理格式串的, %d格式串会被printf解析出, 并从参数中取一个整数,注意,此整数是强制拿sizeof(int)字节的数据,并没有对参数做可适配转换(比如浮点数去掉小数点变成整数之类的友好转换)。
printf 格式串的处理: printf 内部原理和实现 (你想知道的C语言 1.2)
所以,其实5.0f是啥不重要,重要的是5.0f被存储的数据二进制形式是什么.
按照浮点数的IEEE754标准的转换算法,5.0f将被转换成0x40A00000. 所以,上面输出的整数应该就是1084227584.(注意此处的结论不一定是对的!)
5.0f是被转换成0x40A00000, 可以用如下代码验证:
/*
Xi Chen(511272827@qq.com)
cxsjabcabc
*/
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
void dump_data(void *data, int bytes)
{
int i;
unsigned char *p;
p = (unsigned char *)data;
for (i = 0; i < bytes; ++i) {
printf("%x ", p[i]);
}
printf("\n");
}
int main(int argc, char *argv[])
{
float d = 5.0f;
dump_data(&d, sizeof(d));
return 0;
}
https://github.com/cxsjabc/basic/blob/dev/c/_topics/printf/parse_data.c
Q: 实际的运行结果看起来不是上面的数字,而且每次执行还会变动?
A: 汇编代码如下:
可以看到参数5.0f被放到了xmm寄存器中, 此为保存浮点数参数的寄存器. 但是printf内部实作会从堆栈强制找sizeof(int)字节数据,这将是不确定的信息.
在不适用xmm寄存器,而是用堆栈传递参数的编译器环境下,才会得到1084227584.
作者: 陈曦
环境: MacOS 10.14.5
Apple LLVM version 10.0.1 (clang-1001.0.46.4)
Target: x86_64-apple-darwin18.6.0
转载请注明出处