【问题标题】:why does printf() gives random output when it should be 0?为什么 printf() 应该为 0 时会给出随机输出?
【发布时间】:2014-01-14 19:28:37
【问题描述】:

所以 printf() 是一个函数,如果成功则返回写入的字符数,如果发生错误则返回负值,查看此示例,预期的输出为 。 p>

#include <stdio.h>    
int main(void) 
{     
printf("%d");    
return 0;
}

现在当我添加更多这些 %d 时:http://ideone.com/brw5vG 输出变为: 0 134513819 -1216430092 134513808

我无法弄清楚随机垃圾值是怎么回事?输出中也有一个负值,负值证明错误是正确的,那么任何人都可以准确指出这里的错误是什么? 请简明扼要。谢谢。

【问题讨论】:

  • 你从来没有真正检查过printf的返回值。
  • printf 的返回值与其应写入标准输出的值之间存在巨大差异。两种完全不同的东西。
  • @remyabel:这不是问题所在。
  • @remyabel:没有人这样做,而且我想不出任何可以在这里发挥作用的实现。
  • 大多数现代编译器都会对此代码发出警告。

标签: c gcc printf c99


【解决方案1】:

因为"%d" 意味着需要一个整数。你没有通过任何,所以你得到未定义的行为。

请注意,g++ 4.8.2 给出了一个有用的警告:

警告:格式 '%d' 需要匹配的 'int' 参数 [-Wformat=]

对于 clang++ 3.4 也是如此:

警告:转换次数多于数据参数 [-Wformat]

【讨论】:

  • 我不希望printf() 会在这里返回错误结果。不是它不能,只是没有现有的实现会。
  • @DietrichEpp 同意,未定义的行为是未定义的行为。
【解决方案2】:

预期的输出为零

printf("%d");

您不应期待任何事情,因为您的程序会调用未定义的行为。

(C99, 7.19.6.1p2) "[...] 如果格式的参数不足,则行为是 未定义。[...]"

【讨论】:

    【解决方案3】:

    您错误地将格式字符串指定为printf 这是undefined behavior,您应该对结果没有任何期望。通过指定 %d,您是在告诉 printf 期待一个您尚未提供的 int 参数。

    如果我们查看 C99 草案标准部分 7.19.6.1 fprintf 函数,它还涵盖了 pritnf 的格式说明符:

    [...]如果格式的参数不足,则行为未定义。[...]

    【讨论】:

    • 好的,我明白了,但为什么我们首先得到一个零?我的意思是,如果它是“未定义的行为”,那么第一个也应该是一些随机值。
    • 未定义表示行为未定义。任何事情都有可能发生。
    【解决方案4】:

    问题在于你如何提出问题;你假设它“应该是0”。事实上,这是未定义的行为,printf 将替换 %d 堆栈中的任何内容。

    【讨论】:

      【解决方案5】:

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

      C11 标准在7.21.6 部分中说格式化输入/输出函数

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

      您没有为相应的 %d 说明符传递任何参数。

      【讨论】:

      • 但是为什么我们第一个得到一个零?
      • @SyedAmeedul:未定义的行为意味着任何事情都可能发生。打印零是任何事情发生的一个例子。问“为什么”是没有意义的。
      • 正如我已经说过的,万一出现 UB,任何事情都可能发生。不能保证第一名的值总是0。结果可能因编译器而异,甚至因同一编译器的不同版本而异。
      【解决方案6】:

      手头有 2 个问题:首先是为什么编译器不会就这个对 printf() 的错误调用发出错误,其次是为什么会得到垃圾输出。我会一个一个地回答。

      printf() 是一个棘手的功能。虽然大多数函数都有恒定数量的参数传递给它们,但printf() 是不同的。 例如,如果我们采用这个简单的函数:

      int max(int a, int b) {
        if (a > b) return a;
        else return b;
      }
      

      你可以看到这个函数总是接收 2 个参数。这也是编译器知道的,并在您编译代码时强制执行。这就是为什么像max(4) 这样的电话不起作用的原因。编译器会看到我们传递了 max() 1 个参数而不是 2 个参数,它会发出错误。

      printf() 是一个接受可变数量参数的函数,该数量由格式字符串中的格式说明符(以 % 开头的内容)的数量决定。这意味着编译器在编译时无法知道您传递给 printf 的参数数量是否足够(或者太多)。

      你得到垃圾打印的原因是因为函数如何读取它们的输入参数。函数的所有输入参数都驻留在堆栈中。这些在调用函数之前被压入堆栈,然后由函数寻址。在这种情况下,printf() 预计除了格式字符串之外还有一个额外的参数(因为%d),因此它会查找其第二个参数可能所在的地址。唉,那个参数没有被传递,所以它实际上会查找堆栈中可能包含其他任何东西(返回地址、帧指针、封闭范围的局部变量或其他)的位置。

      您可以阅读更多关于函数调用如何工作的信息here

      【讨论】:

      • 这意味着编译器在编译时无法知道您传递给 printf 的参数数量是否足够 这是错误的。当格式字符串是字符串文字时,例如在 OP 示例中,是什么阻止编译器知道尾随参数的数量不正确?
      • 当格式字符串是文字时是编译器知道的特殊情况。但是,一般情况下它无法知道。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-03-17
      • 2016-11-30
      • 2019-04-22
      • 2021-10-25
      • 2016-03-22
      • 2017-06-03
      • 1970-01-01
      相关资源
      最近更新 更多