简体   繁体   English

浮动混乱 - 我很困惑浮点说明符是如何发生的

[英]float confusion- I'm confused how float specifier occurs

Consider the following code snippet 请考虑以下代码段

    float a=12.2;
    printf("%f %d",a,a); //output 12.200000 Garbage value

but

    printf("%d %f",a,a);//Output Garbage value Garbage Value

my question is why in the second printf, both %d and %f gives garbage value. 我的问题是为什么在第二个printf中,%d和%f都给出了垃圾值。 I know it is because I have used %d first?..But can't find any proper explanation.. 我知道这是因为我先用了%d?但是找不到任何正确的解释..

Using a wrong conversion specifier for a data type invokes undefined behavior . 对数据类型使用错误的转换说明符会调用未定义的行为 You may get anything, either expected or unexpected result. 您可能会得到任何预期或意外结果。

C11: 7.21.6 Formatted input/output functions: C11:7.21.6格式化输入/输出功能:

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. 如果转换规范无效,则行为未定义.282)如果任何参数不是相应转换规范的正确类型,则行为未定义。

Using %d to print the value of a ( float ) results in undefined behavior of the program. 使用%d要打印的值afloat在程序的未定义的行为)的结果。

When you use the wrong specifier in printf , things such the following can happen. 当您在printf使用错误的说明符时,可能会发生以下情况。

The caller does this: 来电者这样做:

  • Suppose, for illustration, the stack pointer is currently at 100, and arguments are pushed onto the stack in reverse order. 举例来说,假设堆栈指针当前为100,并且参数以相反的顺序被压入堆栈。
  • For functions with variable argument types, float arguments are converted to double . 对于具有可变参数类型的函数, float参数将转换为double
  • For printf("%d %f"), a, a) , the caller first puts the last a on the stack. 对于printf("%d %f"), a, a) ,调用者首先将最后a放在堆栈上。 The caller makes eight bytes of space by changing the stack pointer to 92, converts a to double , and writes eight bytes to the stack. 调用者通过将堆栈指针更改为92,将a转换为double ,并将8个字节写入堆栈,从而产生8个字节的空间。
  • For the first a , the caller changes the stack pointer to 84 and writes eight bytes to the stack. 对于第一个a ,调用者将堆栈指针更改为84并将八个字节写入堆栈。
  • For the format string, "%d %f" , the caller changes the stack pointer to 80 and writes a four-byte pointer to the stack. 对于格式字符串"%d %f" ,调用者将堆栈指针更改为80并将四字节指针写入堆栈。

Then printf does this: 然后printf这样做:

  • printf initializes a pointer to where the arguments start, 80. printf初始化一个指向参数开始处的指针80。
  • It reads the pointer to the string at 80. 它在80处读取指向字符串的指针。
  • Since a pointer is four bytes, printf updates its argument pointer to point to 84. 由于指针是四个字节,因此printf其参数指针更新为指向84。
  • The string says the next argument is %d , so printf expects a four-byte integer, and it reads four bytes from 84. 该字符串表示下一个参数是%d ,因此printf需要一个四字节整数,它从84读取四个字节。
  • Since an int is four bytes, printf updates its argument pointer to point to 88. 由于int是四个字节,因此printf其参数指针更新为指向88。
  • Since the eight bytes at 84 to 91 contain the encoding of a double , the four bytes from 84 to 87 do not make any sense as an int , and printf prints whatever int value the bits happen to represent. 由于从84到91的八个字节包含double的编码,因此从84到87的四个字节作为int没有任何意义,而printf打印该位恰好表示的int值。
  • The string says the next argument is %f , so printf expects an eight-byte double , and it reads eight bytes from 88. 该字符串表示下一个参数是%f ,因此printf需要一个8字节的double ,它从88开始读取8个字节。
  • The bytes from 88 to 95 contain four bytes from the end of the first double and four bytes fro the start of the second double , so they do not make any sense as a double , and printf prints whatever value the bits happen to represent. 从88到95的字节包含从第一个double的末尾开始的四个字节和从第二个double开始的四个字节,因此它们没有任何意义作为doubleprintf打印出该位碰巧代表的任何值。

This is not the way all C implementations work, but it is one thing that may happen when you use incorrect arguments with printf . 这不是所有C实现的工作方式,但是当您对printf使用不正确的参数时,可能会发生这种情况。 The C standard makes the behavior undefined because it does not control how implementations work, so they might behave as described as above, but they might instead be passing some of the arguments in registers. C标准使行为不确定,因为它不控制实现的工作方式,因此它们的行为可能如上所述,但是它们可能会在寄存器中传递某些参数。 In the latter case, you would see different behaviors, such as printf printing garbage for %d because nothing had set the corresponding integer register to an appropriate value but printf printing the correct value for %f because it looked for it in a floating-point register, and the caller did place the value of a in a floating-point register. 在后一种情况下,您会看到不同的行为,例如%d printf打印垃圾,因为没有将相应的整数寄存器设置为适当的值,但是printf打印了%f的正确值,因为它在浮点中查找它注册,主叫方没有地方的价值a在浮点寄存器。

Because you can't know what will happen. 因为你不知道会发生什么。 If sizeof(float) != sizeof(int) , you will definitely get a garbage value twice in the second case, since printf read either too many bytes or too few bytes from the first argument, so it will screw up when reading the next argument (assuming arguments were passed on stack). 如果sizeof(float) != sizeof(int) ,你肯定会在第二种情况下得到两次垃圾值,因为printf从第一个参数中读取太多字节或太少的字节,所以当读取下一个参数时它会搞砸参数(假设参数在堆栈上传递)。

Another issue that arises is that the argument passed is being converted to double , which usually has a different size from int , and then printf tries to pick an int from something that is double . 出现的另一个问题是传递的参数被转换为double ,它通常具有与int不同的大小,然后printf尝试从double选择一个int There is a size mismatch (see comments below). 尺寸不匹配(请参阅下面的评论)。

Either way, this invokes undefined behaviour, so you really can't "expect" anything meaningful to happen. 无论哪种方式,这都会调用未定义的行为,因此您实际上无法“期待”任何有意义的事情发生。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM