2

阅读有关 ACCU 过载 #115 的有趣文章:“恶魔可能从你的鼻子飞出”,我发现作者说:

在序列点之间,您不得对所涉及变量的状态做出任何假设。这也意味着在 C 中,与大多数其他语言不同,以下表达式会导致未定义的行为

v[i] = i++;

因为赋值运算符不代表 C 中的序列点

有人可以解释这里暗示 UB 的详细推理是什么吗?我认为这将是在两个序列点之间对同一个变量进行多次写入的问题,除了 v[i] 别名 i...

4

3 回答 3

5

这不仅仅是关于写入。当您不知道您是在读取变量的旧值还是新值时,读取也会发挥作用。在这种情况下,无法确定v[i]是指的是 的旧值i还是 的新值i

例如,表达式v[i] = i++可以解释为

  1. 执行任务:v[i] = i
  2. 增量i

或者,或者,它可以解释为

  1. 获取 的旧值ii_old = i
  2. 增量i
  3. 执行任务:v[i] = i_old

如您所见,代码的行为会根据其解释方式而变化。这些只是两种可能的不一致情况的示例。

但是该语言并不将行为限制在如此有限的各种场景中。相反,语言说行为是未定义的,这意味着编译器可以自由地拒绝以任何“可预测的”方式解释此代码。

于 2013-07-03T21:37:23.293 回答
3

i在同一个序列点内有一个读和一个写。也就是说,i++可以在计算 的地址之前或之后改变 i v[i]。事实上,由于这种行为是未定义的,它很可能不会做这两件事——例如,编译器可以假定该语句不可访问并将其删除。(或者让恶魔从你的鼻子里飞出来等等)

于 2013-07-03T21:32:29.613 回答
3

您是正确的,在没有干预序列点的情况下多次修改变量会导致未定义的行为。但是还有一个额外的要求,即仅访问变量的值以确定要修改的值。这是不允许的第二个要求

v[i] = i++;

这里对数组索引的 ' 值的访问与对( )i的修改无关。由于两次访问之间没有中间序列点,因此这是未定义的行为。ii++

于 2013-07-03T21:37:15.917 回答