c++ - C语言中printf输出的奇怪错误
巴扎黑
巴扎黑 2017-04-17 11:42:34
0
2
689

源代码很简单,就是定义一个float变量a=2.5,int变量b=2

然后将a,b分别按%d %f型输出。
当然没有按正确类型格式化输出肯定是有问题的。
但是按正确类型格式化输出也发生了很奇怪的问题。
源代码如下,VS2013下编译通过0 errors, 0 warnings。

#include <stdio.h>
int main(void)
{

float a = 2.5;
int b = 2;


printf("%d\n%f\n%d\n%f\n", a, a, b, b);    /*这条语句的输出全是错的*/
putchar('\n');

printf("%f\n%d\n%f\n%d\n", a, a, b, b);    /*这条语句的输出有两个对的两个错的,符合我的预期*/

return(0);}

输出结果如下图

我的主要问题是,为什么第一个printf的输出没有一个是对的?
本人是小白,希望能有大神帮忙解答,多谢!

巴扎黑
巴扎黑

reply all(2)
伊谢尔伦

First of all, float will be automatically converted to double here. To facilitate understanding, we assume here that sizeof(int) is 4 and sizeof(double) is 8.

Then, for this sentence:

printf("%d\n%f\n%d\n%f\n", a, a, b, b);

According to %dn%fn%dn%f to read, the data and read pointer in the stack will appear as follows:

  (int)a_>|     |4
          |_2.5_|4
(float)a_>|     |4
  (int)b_>|_2.5_|4
          |__2__|4
(float)b_>|__2__|4

So, what the first %d reads is actually the first 4 bytes of 2.5. Immediately after the first %f reads the last 4 bytes of 2.5, plus the first 4 bytes of the next 2.5. Similarly, the second %d reads the last 4 bytes of 2.5, and the second %f What I read is two 2.

Okay, it’s a bit messy. How does the C language define this phenomenon of "type random reading" in the output?

7.21.6 Formatted input/output functions:

If a conversion specification is invalid, the behavior is undefined.282) If any argument is not the correct type for the corresponding conversion specification, the behavior is undefined.

As the standard says, this is UB (Undefined Behavior), so there is no need to go into why it outputs the four numbers in your screenshot. The results of different compilers are also different.


Supplement

The principle has been analyzed above, so you can also analyze why your second sentence can output what you want.

According to %fn%dn%fn%d to read, the data and read pointer in the stack will appear as follows:

          |     |4
(float)a_>|_2.5_|4_ _ _ _ _ _ correct!
  (int)a_>|     |4
          |_2.5_|4
(float)b_>|__2__|4
  (int)b_>|__2__|4_ _ _ _ _ _ correct!

As luck would have it, the bull's head hits the horse's mouth, and you get the output you want. . .

Don’t take chances and go by the type honestly printf. This is the correct attitude for beginners.

洪涛

Please pay attention to the stdarg.h file in the standard library. It defines several macros, namely va_start, va_arg, and va_end. Several macros are used to transfer indefinite parameters. Special attention should be paid to the fact that va_arg relies on its second parameter (t, type) when obtaining the passed in parameters. If the type does not correspond, the entire variable parameter transfer will not be parsed normally (misaligned). Printf depends on vsprintf, and the implementation of vsprintf relies on these macros. It will call va_arg according to the format string (that is, those %d %f...). If your format string does not match your subsequent variable parameters, Correct correspondence, then there may be misalignment when obtaining the indefinite parameters (and your second output is correct twice because it happened to be correct again after being misaligned twice).

There is also a float (32-bit) parameter that will be automatically converted into a double (64-bit) when passing printf (this is why there is no difference between %f and %lf), and the int parameter occupies 32 bits.

After laying the groundwork above, let’s explain your question below.
Environment: Windows XP 32, gcc version 3.4.0 (mingw special)

Note: The following continuous hexadecimal data from left to right corresponds to the memory address from low to high

The parameters you passed in a,a,b,b correspond to hexadecimal 00 00 00 00 00 00 04 40 00 00 00 00 00 00 04 40 02 00 00 00 02 00 00 00The correct segmentation should be:

a: [00 00 00 00 00 00 04 40]
a: [00 00 00 00 00 00 04 40]
b: [02 00 00 00]
b: [02 00 00 00]

According to the format string you output for the first time, printf does the following segmentation:

%d: [00 00 00 00] int,对应0x00000000,也就是0了
%f: [00 00 04 40 00 00 00 00] double,0.0,(看double的表示法)
%d: [00 00 04 40] int,对应0x40040000,也就是10进制的1074003968
%f: [02 00 00 00 02 00 00 00] double,0.0

So the output is:

0
0.000000
1074003968
0.000000

According to the format string you output for the second time, printf does the following segmentation:

%f: [00 00 00 00 00 00 04 40] double, 对应2.5
%d: [00 00 00 00] int 对应0
%f: [00 00 04 40 02 00 00 00]   double,对应0
%d:[02 00 00 00] int 对应0

That’s what you see:

2.500000
0
0.000000
2

As for why double changes to 0.0 several times, you need to look at the IEEE double representation standard.

In short: printf will obtain the passed in variable parameters according to the format string when outputting, and the wrong format string will lead to an error in obtaining the variable parameters, resulting in an output error.

Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template