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

时间:2014-01-24 10:01:43

标签: c floating-point

请考虑以下代码段

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

但是

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

我的问题是为什么在第二个printf中,%d和%f都给出了垃圾值。我知道这是因为我先用了%d?但是找不到合适的解释..

3 个答案:

答案 0 :(得分:4)

对数据类型使用错误的转换说明符会调用undefined behavior。您可能得到任何预期或意外结果。

C11:7.21.6格式化输入/输出功能:

  

如果转换规范无效,则行为未定义.282)如果有任何参数   不是相应转换规范的正确类型,行为是   未定义。

使用%d打印afloat)的值会导致程序的未定义行为。

答案 1 :(得分:2)

当您在printf中使用错误的说明符时,可能会发生以下情况。

来电者这样做:

  • 假设,为了说明,堆栈指针当前为100,并且参数以相反的顺序被压入堆栈。
  • 对于具有可变参数类型的函数,float参数将转换为double
  • 对于printf("%d %f"), a, a),调用者首先将最后一个a放在堆栈上。调用者通过将堆栈指针更改为92,将a转换为double,并将8个字节写入堆栈,从而产生8个字节的空间。
  • 对于第一个a,调用者将堆栈指针更改为84并将八个字节写入堆栈。
  • 对于格式字符串"%d %f",调用者将堆栈指针更改为80并将四字节指针写入堆栈。

然后printf执行此操作:

  • printf初始化一个指向参数开始位置的指针,80。
  • 它在80处读取指向字符串的指针。
  • 由于指针是四个字节,printf将其参数指针更新为指向84。
  • 字符串表示下一个参数是%d,因此printf需要一个四字节整数,它从84开始读取四个字节。
  • 由于int是四个字节,printf将其参数指针更新为指向88.
  • 由于84到91的8个字节包含double的编码,因此从84到87的四个字节没有任何意义intprintf打印任何内容这些位碰巧代表int值。
  • 字符串表示下一个参数是%f,因此printf需要一个8字节的double,它从88开始读取8个字节。
  • 88到95之间的字节包含第一个double末尾的四个字节和第二个double开头的四个字节,所以它们没有任何意义{{1 }和double打印出比特碰巧代表的任何值。

这不是所有C实现的工作方式,但是当您使用printf的错误参数时,可能会发生一件事。 C标准使行为未定义,因为它不控制实现的工作方式,因此它们的行为可能如上所述,但它们可能会在寄存器中传递一些参数。在后一种情况下,您会看到不同的行为,例如printfprintf打印垃圾,因为没有任何东西将相应的整数寄存器设置为适当的值,但%d打印{的正确值{1}}因为它在浮点寄存器中查找它,并且调用者确实将printf的值放在浮点寄存器中。

答案 2 :(得分:1)

因为你不知道会发生什么。如果sizeof(float) != sizeof(int),在第二种情况下肯定会得到两次垃圾值,因为printf从第一个参数读取太多字节或太少字节,所以在读取下一个参数时会搞砸(假设参数在堆栈上传递)。

出现的另一个问题是传递的参数被转换为double通常的大小与int不同,然后printf次尝试从int的内容中选择double。尺寸不匹配(请参阅下面的评论)。

无论哪种方式,这都会调用未定义的行为,所以你真的无法“期待”任何有意义的事情发生。