【问题标题】:How does `printf()` distinguish between floating point arguments and integer type arguments?`printf()` 如何区分浮点参数和整数类型参数?
【发布时间】:2018-08-04 11:04:20
【问题描述】:

如果我有

printf("%f\n", 3);

我明白了

3.000000

如果我有:

printf("%f\n", 3.0);

我得到了同样的答案。

但事情是这样的,3 和 3.0 应该是完全不同的,有点明智。

3.0 应该类似于 01000000010000000000000000000000 (但由于 vararg 函数中的隐式转换而双精度)
3 应该类似于00000000000000000000000000000011

3 在显示之前必须以某种方式转换为浮点数。

所以我的问题是,这是怎么做到的?编译器会以某种方式作弊吗?我将如何在我自己的变量 args 函数中实现这一点?

【问题讨论】:

  • 它没有。 printf("%f\n", 3); 是未定义的行为。
  • 你用的是什么编译器?刚刚在onlinegdb.com/BymQYx1OG 尝试过,它输出 0.00000。但正如所说的那样,这是未定义的行为——这意味着编译器可以做它想做的事情,包括你所说的“作弊”,但这对于编译器为你“修复”损坏的代码来说是奇怪的(而且无益的)行为。将编译器警告级别设置为高。一些编译器可以选择显式测试标准函数的格式说明符,也可以将新函数添加到使用相同格式说明符的检查中。
  • 哪个编译器在哪个平台上为整数 3 生成 3.000000?由于它是一个常数,编译器在编译时评估表达式的外部机会(不太可能,但几乎可能)。尝试使用整数变量而不是文字,您应该会得到不同的结果。
  • "我将如何在我自己的变量 args 函数中实现它?":你真的不想那样做!而是使用编译器的工具来验证格式说明符。
  • 在 SO 的 cmets 中出现正确答案似乎很常见。谢谢!我在 Linux 上使用 GCC 7。我没有用另一个编译器尝试过。当我在 XV6 中重新实现 printf 的 %f 时,GCC 似乎不想再作弊了:/

标签: c floating-point printf


【解决方案1】:

printf() 如何区分浮点参数和整数类型参数?

printf() 依赖调用代码根据其格式传递预期类型的对象。


printf() 分析格式字符串,在本例中为"%f\n"

"%f" 引导代码预计下一个参数是double

如果 float 被传递,它会被转换为 double,作为通常将参数转换为函数的 ... 部分的一部分。

通过调用代码传递的float/doubleprintf() 将正确地以double 的形式获取该数据并打印它。 如果调用代码传递了其他类型,则结果是未定义行为 (UB)@Eric Postpischil 很好地解释了这个 UB。

这是怎么做到的?编译器会以某种方式作弊吗?

如何在我自己的变量 args 函数中实现这一点?

OP 看到的意外未定义行为不一定可以用用户代码重现

【讨论】:

    【解决方案2】:

    如果您的源代码包含 printf("%f\n", 3); 并且结果输出是“3”,那么最有可能发生的情况是:

    • 在您的程序的其他地方有一个浮点数 3,例如像 x = 3.; 这样的赋值语句或像 printf("%f\n", 3.0); 这样的另一个 printf 调用。
    • 为了使用该 3,编译器将其加载到浮点寄存器中。它可能已经为您的printf 之前的代码或为您的printf 之后的代码做准备。
    • 在评估 printf("%f\n", 3); 时,当使用 %f 转换说明符调用 printf 时,浮点 3 位于 double 参数应该位于的寄存器中。
    • 因此,由于用于为%f 传递参数的寄存器恰好有一个浮点数 3,因此printf 使用了该值并产生了“3”的结果。

    换句话说,您的程序碰巧“工作”很大程度上是偶然的,而不是设计的。实际上,您确实错误地调用了printf,并且它只产生了“3”,因为恰好发生了无关的因素。如果该浮点寄存器中的值是 5 或 -3.25,则 printf 将打印该值。

    printf 例程无法确定您传递给它的值的类型。 (好的编译器会在编译时检查类型,如果格式字符串是文字[而不是在运行时计算的东西]。但这是由编译器查看调用的源代码完成的,而不是由 @ 987654335@ 例程本身。)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-01-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多