【问题标题】:unexpected behavior of C operands evaluation orderC 操作数评估顺序的意外行为
【发布时间】:2013-03-07 02:22:15
【问题描述】:

所以只是为了好玩,我有这个代码sn-p:

#include <stdio.h>
main()
{
 int i;
 int a;
 i = 17;
 //scanf("%d", &i);
 a = (i+=5) * (i-=3);
 printf("a is %d, i is %d\n", a, i);
}

在 C 规范中,它说操作数评估的顺序是 undefined,所以我希望看到 22 * 19 或 19 * 14。但是,结果是 19 * 19:

~ $ gcc a.c
~ $ ./a.out
a is 361, i is 19

我想了想,我能想到的唯一解释是编译器对(i+=5) 的值进行了“延迟”评估,它认为(i+=5) 的值只是 i 的值. (i-=3) 也一样。

但是,如果我取消注释 scanf():

#include <stdio.h>
main()
{
 int i;
 int a;
 i = 17;
 scanf("%d", &i);
 a = (i+=5) * (i-=3);
 printf("a is %d, i is %d\n", a, i);
}

现在我在提示符下输入 17:

~ $ gcc a.c
~ $ ./a.out
17
a is 418, i is 19

为什么会表现出不同的行为?

【问题讨论】:

  • 为什么你会对未定义的行为导致不可预测的行为感到惊讶?
  • 未定义。它可以为所有 gcc 关心打印 0。
  • 行为不同正是因为上述行为是未定义,正如您已经指出的那样。
  • 我不同意投反对票。这个人努力调查他们不理解的行为,解释清楚并提出问题。不要因为他们不理解“未定义行为”的范围而责备他们。请注意,他们希望评估的顺序是未定义的,而不是整个操作。
  • 如果您的一天压力很大,有时最好不要在 StackOverflow 上发表评论。

标签: c evaluation operands


【解决方案1】:

这是未定义的行为。 C 标准规定,不允许在两个序列点(在这种情况下,语句前后的分号)之间多次修改同一个对象(在这种情况下为 i)。

未定义的行为意味着任何事情都可能发生。它看起来可以正常工作。它可能会给出错误的答案。它可能会使您的进程崩溃。它可以擦除您的硬盘驱动器。或者它甚至可以让你的 CPU 着火。根据语言标准,这些都是允许的行为。

【讨论】:

  • 或者它可以让你喝咖啡!
  • 它也会导致鸡无头乱跑。 +投票如果你能找到在接下来的五分钟内呈现这个 UB 的 n1570.pdf 部分,@AdamRosenfield ;)
【解决方案2】:

可能主要区别在于,在您的第一种情况下,i 可以在其整个生命周期内进行预测,因此优化器会预先计算它并将变量替换为常数值。

在您的第二个示例中, scanf 使优化变得不可能。编译器无法提前知道 i 的值是什么,并执行您所期望的,这是标准未定义的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2013-07-28
    • 1970-01-01
    • 1970-01-01
    • 2011-07-10
    • 2018-11-05
    • 1970-01-01
    • 2020-09-14
    • 1970-01-01
    相关资源
    最近更新 更多