天天看点

printf 格式串和参数不匹配的后果(你想知道的C语言 1.10)

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([email protected])
   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: 汇编代码如下:

printf 格式串和参数不匹配的后果(你想知道的C语言 1.10)

    可以看到参数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
 
转载请注明出处