【问题标题】:Why does the output of each printf in this code vary? [duplicate]为什么这段代码中每个 printf 的输出都不同? [复制]
【发布时间】:2012-03-21 18:49:13
【问题描述】:

可能重复:
Why does printf print wrong values?

#include<stdio.h>

int main(int argc, const char *argv[]){
    float f1 = 125.2f;

    printf("\n [%f] [%d] [%g]",f1,f1,f1);
    printf("\n [%f] [%g] [%d]",f1,f1,f1);
    printf("\n [%g] [%f] [%d]",f1,f1,f1);
    printf("\n [%g] [%d] [%f]",f1,f1,f1);
    printf("\n [%d] [%g] [%f]",f1,f1,f1);
    printf("\n [%d] [%f] [%g]",f1,f1,f1);  
    getchar();
}  

我可以看到每个 printf 的不同输出,尽管使用相同的格式标识符打印相同的变量。

【问题讨论】:

  • 这个问题能被问多少次!?
  • 这些是格式字符串。你应该阅读printf()。
  • 首先,这不是 C,因为 main() 不返回 void,而是 int。其次,clrscr() 方法不是 C 标准的一部分。第三,这不是您正在处理的objective-c,而是纯C。第四,请在发布之前处理您的代码格式。
  • @CarlNorum 人们会在不知道任何东西的情况下开始编程,包括如何进行自己的基础研究(这也包括知道他们可以/应该进行自己的研究)。这可能是一个非常庞大的人群。

标签: c


【解决方案1】:

printf 不是类型安全的,因此使用不正确的格式描述符会导致 Undefined Behavior。

未定义的行为意味着任何可观察到的行为都是可能的,因为代码不遵守标准规定的规则。

【讨论】:

  • 不可观察的行为也是可能的。
【解决方案2】:

您的代码会导致未定义的行为。任何事情都有可能发生。

根据规范,J.2 未定义行为部分:

在调用格式化输入/输出函数之一时,格式参数不足,或者参数没有适当的类型(7.19.6.1、7.19.6.2、7.24.2.1、7.24.2.2)。

因此,您还可以查看参考部分以获取更多信息。在您的情况下,相关位是 7.9.16.1 fprintf 函数,第 9 段:

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

和7.19.6.3 printf函数:

printf 函数等效于 fprintf 参数 stdout 插入到 @ 的参数之前987654326@.

【讨论】:

    【解决方案3】:

    "%d" 格式需要 int 参数。传递 float 参数(提升为 double)会导致未定义的行为。

    可能发生的事情是 printf 从堆栈中获取了错误数量的字节,但是您最好花时间修复代码,而不是找出为什么错误的代码会以特定方式出现错误行为。

    而void main() 是错误的;使用int main(void)。如果某些教科书告诉您void main() 是正确的,那么作者对语言的了解还不够充分,无法撰写有关它的内容。

    【讨论】:

      【解决方案4】:

      细节会因机器和编译器而异,但假设 float 为 4 字节,double 为 8 字节,int 为 4 字节。

      您将三个浮点数放在堆栈上,它们被提升为 8 个字节,总共 24 个字节。然后你的格式字符串从堆栈中提取不同数量的字节。所以

      • %f, %d, %g 将拉取 8、4、8 个字节
      • %f、%g、%d 将拉取 8、8、4 个字节
      • %g、%f、%d 将拉取 8、8、4 个字节
      • %g、%d、%f 将拉取 8、4、8 个字节
      • %d, %g, %f 将拉取 4、8、8 个字节
      • %d、%f、%g 将拉取 4、8、8 个字节

      printf 的 vararg 区域中的类型必须与转换运算符所期望的类型相匹配。

      【讨论】:

      • 这完全取决于实现。例如,在 Mac 上尝试使用 64 位应用程序。
      • 请注意我的第一句话:“细节会因机器和编译器而异”。然后我声明了 float、double 和 int 的“假设”特定大小。
      • 我的观点是,假设大小是不够的。在 Mac 上(也许是 AMD/Linux 64 位 ABI 的一切),浮点和整数可变参数位于独立的逻辑堆栈上。在这种情况下,谈论从单个堆栈中提取字节是没有意义的。
      • 这是一个可能出错的例子,而不是一个如何正确处理的例子。因此,我关于 vararg 匹配类型的最后陈述仍然有效。但是你关于逻辑堆栈的观点很好。
      猜你喜欢
      • 1970-01-01
      • 2015-09-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-02-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多