使用printf打印出浮动值

#include #include int main(void) { int x, *ptr_x; float f , *ptr_f; ptr_f = &f; ptr_x = &x; *ptr_x = 5; *ptr_f = 1.5; //printf("%d %f\n", f,x); printf ("\n\nxd = %d \t xf = %f \n ff = %f \t fd = %d", x,x,f,f); return 0; } 

ff =%f的输出不是预期的。

xd = 5 xf = 0.000000
ff = 0.000000 fd = 1073217536

此代码的要点是显示如果使用%d打印浮动值并且打印int值为%f将会发生什么。

即使我使用%f,为什么浮点值无法正确打印?

printf()不是类型安全的。

传递给printf()的参数将根据对编译器的承诺进行处理。

此外,当通过可变参数传递时, float s被提升为double

因此,当您第一次(对于xf )承诺编译器%f时,编译器会从参数中吞噬整个double (通常是8个字节),在此过程中吞噬float。 然后第二个%f切入第二个双尾的零尾数。

这是你的论点的图片:

 +-0-1-2-3-+-0-1-2-3-+-0-1-2-3-4-5-6-7-+-0-1-2-3-4-5-6-7-+ | x | x | f | f | +---------+---------+-----------------+-----------------+ %d--------|%f----------------|%f---------------|%d------| 

但是f看起来像这样(已被提升为double ):

 f = 3FF8000000000000 

让我们再次用值绘制它,并推测你的机器字节序:

 | 05000000 | 05000000 | 00000000 0000F83F | 00000000 0000F83F | | %d, OK | %f, denormal... | %f, denormal... | %d, OK | 

请注意,1073217536是0x3FF80000。

一旦你将至少一个无效格式说明符传递给printf (比如尝试使用%d打印float值或使用%f打印一个int值), 整个程序就会被修复无法修复。 这种破坏性行动的后果可以在计划的任何地方看到。 在您的情况下,尝试使用无效的格式说明符打印某些内容会导致即使有效的格式说明符停止工作。

正式地说,你写了一个展示未定义行为的程序。 它可以完全不可预测地行动。 你自己说的

此代码的要点是显示如果使用%d打印浮动值并且打印int值为%f将会发生什么。

你观察到的破碎行为正好certificate了这一点! 一个奇怪且不可预测的表演程序正是当你尝试做类似的事情时会发生的事情。

试试这个:

 printf("size of int = %d, size of float = %d, size of double = %d\n", sizeof(int), sizeof(float), sizeof(double)); 

当您调用printf() ,系统会将参数压入堆栈。 所以堆栈看起来像这样:

 pointer to format string [probably 4 bytes] x [probably 4 bytes] x [probably 4 bytes] f [probably 6 or 8 bytes] f [probably 6 or 8 bytes] 

然后printf()在解析格式字符串时从堆栈中弹出字节。 当它看到%d它会为int弹出足够的字节,当它看到%f它会为浮点弹出足够的字节。 (实际上,当浮点数作为函数参数传递时,它们会被提升为双精度数,但重要的是它们需要的字节数多于整数。)因此,如果你对参数“撒谎”,它会弹出错误的字节数并盲目地根据您的指示转换它们。

因此,它将首先为xd弹出正确的字节数,因为您已正确地告诉它x是一个int。

但是它会为浮点数弹出足够的字节,这将消耗堆栈中第二个x和第一个f一部分,并将它们解释为xf的浮点数。

然后它将弹出足够的字节用于另一个浮点数,这将消耗第一个f的剩余部分和第二个f一部分,并将它们解释为ff的浮点数。

最后,它将为int弹出足够的字节,这将消耗第二个f的剩余部分,并将它们解释为fd的int。

希望有所帮助。