2013-07-03 144 views
2

阅读ACCU的超载#115一篇有趣的文章:“魔鬼可能飞出你的鼻子:”我发现作者说:为什么v [i] = i ++在C中是未定义的行为?

序列之间的点你不能作出关于参与变量的状态做任何假设。这也意味着在C中,与大多数其他语言不同,以下表达式导致未定义行为

v [i] = i ++;

因为赋值运算符用C

并不代表一个序列点

有人可以解释有什么详细的理由,这里意味着UB? 我认为这将是一个问题,有两个以上的序列点之间写入同一个变量,这是我在这里看不到的,除了v [i]可能会混淆我...

+1

这可能会帮助你。 http://stackoverflow.com/questions/13744507/why-is-i-vi-undefined –

+0

[任何人都可以解释这些未定义的行为(i = i ++ + ++ i,i = i ++等等) ](http://stackoverflow.com/questions/949433/could-anyone-explain-these-undefined-behaviors-iiiii-etc) –

+0

该段解释了为什么,“序列点之间,你不能做任何关于涉及变量的状态“,并且'v [i]'假设'i'会有一些特别有意义的值。 –

回答

3

你是正确的,修改一个变量不止一次没有中间顺序点导致未定义的行为。但还有一个附加要求,即变量的值只有访问才能确定要修改的值。它是不允许

v[i] = i++; 

这里i的价值为数组索引访问无关与修改被以ii++)进行的第二个要求。由于两次访问之间没有中间顺序点,所以这是未定义的行为。

+1

实际上,形式上的要求是“前值 只能读取以确定要存储的值*”。 *存储*,不*修改*。不幸的是,这个要求在一些更棘手的情况下受到解释。例如,如果数组'a [a [i]] = i'的表达式应该被定义或未定义,如果数组'a'的初始值是'{0,1,2,3,4 ''。这是排序规则在最新的C和C++规范(C11,C++ 11)中完全重新设计的原因之一。 – AnT

+0

@AndreyT我认为,对于我的情况来说,最具相关性的条款是1.8.15。尤其是“如果对标量对象的副作用相对于使用相同标量对象的值计算的值不确定,则行为未定义”。我用“i ++”解释为“标量对象的副作用”和“v [i]”作为“使用相同scalr对象的值计算值”。 – abigagli

3

有一个读并在相同的序列点内写入i。也就是说,在计算v[i]的地址后,i++可能会在OR之前发生变异。事实上,因为这种行为是未定义的,所以它可以很好地完成这些任务 - 例如,编译器可以假设语句无法访问并将其删除。 (或者让恶魔飞出你的鼻子等)

5

这不仅仅是写道。当您不知道您是否正在读取变量的旧值或新值时,读取也会起作用。在这种情况下,无法说明v[i]是指旧的价值i还是新的价值i

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

  1. 执行分配:v[i] = i
  2. 增量i

,或者可选地,它可以被解释为

  1. 获取的旧值:i_old = i
  2. 增量i
  3. 进行分配:v[i] = i_old

正如你可以很容易地看到,该代码的行为变化取决于它是如何解释。这些只是两个可能不一致情况的例子。

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

+0

这个例子非常有用,我希望能够将多个标记作为接受的答案,因为这与Praetorian的一样好,但我选择他是因为明确引用了额外要求“变量的值只有被访问才能确定要修改的值“是什么给了我正在寻找的具体推理。非常感谢 – abigagli

+0

@abigagli:没问题,只要记住恰当的措辞实际上是“只能访问以确定要存储的值*”。另外,请参阅我对Praetorian的回答的一些额外细节的评论。 – AnT

相关问题