【问题标题】:Does printf() depend on order of format specifiers?printf() 是否取决于格式说明符的顺序?
【发布时间】:2010-08-01 19:30:00
【问题描述】:
#include<stdio.h>
main()
{
    float x=2;
    float y=4;
    printf("\n%d\n%f",x/y,x/y);
    printf("\n%f\n%d",x/y,x/y);
}

输出:

0 
0.000000
0.500000 
0

用 gcc 4.4.3 编译 程序退出,错误代码 12

【问题讨论】:

  • 我很想修改这个问题 - 仅用于在字符串开头使用“\n”。否则,这就是为什么激活编译器警告总是个好主意。
  • 说我笨,但是字符串开头的换行符是怎么回事?
  • @dmckee 废话,您可以在字符串的任何位置放置换行符。见鬼,你可以在字符串中穿插多个换行符 - 非常好的 C 代码。
  • @Neil:你可以。然后你忘记像上面的代码一样添加清理行,你给我留下了0dmckee@thorin$ 作为提示,并在输出的开头添加了一个完全不必要的额外行。根据需要在中间放置尽可能多的换行符,当它使代码循环更清晰时使用前导换行符,但当它们等效时,更喜欢结束换行符。
  • ISO C99 在第 7.19.2p2 节中指出,如果文本文件不以换行符结尾,则不能保证最后一行出现在输出中。

标签: c floating-point double printf


【解决方案1】:

正如其他答案所述,这是因为格式字符串和参数类型不匹配。

我猜你在这里使用的是 x86(基于观察到的结果)。

参数在堆栈上传递,x/y,虽然是 float 类型,但将作为 double 传递给可变参数函数(由于类型“提升”规则)。

int 是 32 位值,double 是 64 位值。

在这两种情况下,您都传递了 x/y (= 0.5) 两次。此值的表示形式为 64 位 double,为 0x3fe0000000000000。作为一对 32 位字,它存储为 0x00000000(最低有效 32 位),后跟0x3fe00000(最高有效 32 位)。所以堆栈上的参数,如printf() 所见,如下所示:

0x3fe00000
0x00000000
0x3fe00000
0x00000000  <-- stack pointer

在您的两种情况中的第一种情况下,%d 导致第一个 32 位值 0x00000000 被弹出并打印。 %f 弹出接下来的两个 32 位值,0x3fe00000(64 位 double 的最低 32 位),然后是 0x00000000(最高位)。生成的 64 位值 0x000000003fe00000(解释为 double)是一个非常小的数字。 (如果您将格式字符串中的%f 更改为%g,您会发现它几乎为0,但不完全是)。

在第二种情况下,%f 正确弹出第一个double,%d 弹出第二个0x00000000 的一半double,所以它似乎可以工作。

【讨论】:

    【解决方案2】:

    当您在 printf 格式字符串中说 %d 时,您必须传递一个 int 值作为相应的参数。否则行为未定义,这意味着您的计算机可能会崩溃或外星人可能会敲门。 %f 和 double 类似。

    【讨论】:

    • 生活让我学会了总是像这样转换所有 printf 参数: const charp = "stop";诠释我=7; printf("Hello %s %d", (const char)p, (int)i);
    • 这也很危险。我更喜欢放弃强制转换,让编译器检查类型是否匹配。 GCC 在这方面做得很好。在 C 中,您永远无法确定 (const char *) 不会意外地将 int 转换为指针。从语法上讲,这是可能的。
    【解决方案3】:

    是的。参数从可变参数列表中读取到 printf 的顺序与读取格式说明符的顺序相同。

    两个 printf 语句都无效,因为您使用的格式说明符需要一个 int,但您只给它一个 floatdouble。

    【讨论】:

    • 但是为什么它的行为在两个语句中都发生了变化?
    • @blacktooth:不知道。当您调用未定义的行为时,编译器不需要做任何明智的事情。
    • @Billy 我告诉那个问我这个问题的人。 :D 他告诉我这是一个面试问题!
    • @blactooth 我参加过很多采访,我已经为他们重新编写了他们的“测试”,所以他们是正确的,然后告诉他们“不,谢谢”——一家要求糟糕的面试问题是一个糟糕的工作。
    • @Billy,对于 nitpick,由于默认的提升规则,您实际上是在将一个 double 传递给 printf 函数。
    【解决方案4】:

    你正在做的是不自信的行为。你所看到的只是巧合; printf 可以写任何东西。

    在提供printf 参数时,您必须匹配确切的类型。你可以例如演员:

    printf("\n%d\n%f", (int)(x/y), x/y);
    printf("\n%f\n%d", x/y, (int)(x/y));
    

    【讨论】:

      【解决方案5】:

      这个结果并不奇怪,在第一个 %d 中,您传递了一个预期为整数的双精度数。

      【讨论】:

      • @Strager:都是正确答案;我对他们所有人都投了赞成票。只是想不出其他原因。
      【解决方案6】:

      http://en.wikipedia.org/wiki/Format_string_attack

      与我的问题有关的事情。支持马修的回答。

      【讨论】:

        猜你喜欢
        • 2017-11-16
        • 2013-06-22
        • 2013-06-22
        • 2012-05-17
        • 2012-05-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多