printf()函数是否依赖于格式说明符的顺序?

4
#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。


2
我有一种冲动要将问题的评分降低 - 只因为在字符串开头使用了“\n”。否则,这就是为什么始终保持编译器警告激活的好主意。 - Dummy00001
4
麻烦称呼我为ChatGPT。关于字符串开头的换行符有什么问题?虽然我可能很蠢,但我还是想知道。 - Brian Hooper
2
@dmckee,胡说八道,字符串中可以随意插入换行符。事实上,在字符串中嵌入多个换行符是合法的C代码。 - anon
1
@Neil:你可以这样做。然后你忘记像上面的代码一样添加清理行,留下了0dmckee@thorin$作为我的提示,以及在输出开头添加了一个完全不必要的额外行。在中间放置尽可能多的换行符,当需要更清晰的循环编码时使用前导换行符,但是在等效时首选结束换行符而不是前导换行符 - dmckee --- ex-moderator kitten
1
ISO C99 在第7.19.2p2节中指出,如果文本文件没有以换行符结束,则不能保证最后一行出现在输出中。 - Roland Illig
显示剩余4条评论
6个回答

16

正如其他答案中所指出的那样,这是由于格式字符串与参数类型不匹配。

我猜测您在使用 x86(根据观察到的结果)。

参数通过堆栈传递,虽然 x/yfloat 类型,但由于类型“提升”规则,将作为 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 弹出第二个 double 中的 0x00000000 半部分,因此它看起来可以工作。

7
当你在printf格式字符串中使用“%d”时,你必须传递一个int值作为相应的参数。否则行为是未定义的,这意味着你的电脑可能会崩溃或外星人可能会敲门。对于“%f”和“double”也是如此。

1
生活教会我始终将printf的所有参数强制转换,如下所示: const char* p = "stop"; int i = 7; printf("Hello %s %d", (const char*)p, (int)i); - danatel
这也很危险。我更喜欢省略转换并让编译器检查类型匹配。GCC在这方面做得非常好。在C中,你永远无法确定(const char *)不会意外地将int转换为指针。从语法上讲,这是可能的。 - Roland Illig

4

是的。printf函数从可变参数列表中按照格式说明符读取顺序读取参数。

这两个printf语句都是无效的,因为您正在使用期望int的格式说明符,但您只提供了一个double。


但是为什么它的行为在两个语句中都会改变呢? - blacktooth
4
@blacktooth:不知道。当您调用未定义的行为时,编译器不需要执行明智的操作。 - Billy ONeal
@Billy 我告诉这个问我问题的人同样的答案。:D 他告诉我这是一个面试题! - blacktooth
@蓝牙 在我参加的许多面试中,我曾经为他们“测试”重新编写过,以使其正确,并告诉他们“不用了” - 一个问糟糕面试问题的公司是不值得为之工作的。 - anon
@Billy,挑剔一点,由于默认的提升规则,你实际上是将一个double传递给printf函数。 - ergosys

3

您正在进行未定义的行为。您所看到的是巧合; printf 可以写入任何内容。

在给 printf 参数时必须匹配精确类型。例如,您可以进行强制转换:

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

2

这个结果并不让人意外,在第%d次中您传递了一个double类型的值,但实际上需要的是整型。


@Strager:它们都是正确的答案;我赞同了所有的回答。只是想不出其他的原因。 - Billy ONeal

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接