我无法理解C99中的一些句子

air*_*ert 6 c c99 language-lawyer

在C99 6.5中说:

在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的计算来修改一次.此外,先前的值应该只读以确定要存储的值

什么"此外,先前的值只能读取以确定要存储的值"是什么意思?在C99中,为什么a[i++] = 1未定义的行为?

Pas*_*uoq 8

a[i++] = 1定义(除非有其他原因未定义,而不是副作用的排序:超出限制的访问,或未初始化i).

你的意思是a[i++] = i,这是未定义的行为,因为它i在相同的序列点之间进行读取i++,从而改变它.

"此外,先前的值应该只读以确定要存储的值"部分意味着i = i + 1;允许,尽管它是从中读取i和修改的i.

另一方面,a[i] = (i=1);是不允许的,因为尽管i只写了一次,但读取i并不是为了计算存储的值.


Kei*_*son 8

"先前值应只读以确定要存储的值",措辞无疑是违反直觉的; 为什么值的读取目的是什么?

该句的要点是强制要求哪些结果取决于哪些操作.

我会从帕斯卡的答案中偷走一些例子.

这个:

i = i + 1;
Run Code Online (Sandbox Code Playgroud)

很好.i在相同的表达式中读取和写入,没有中间序列点,但是没关系,因为在读取完成之后才能进行写入.在完全评估i + 1表达式及其子表达式之前,无法计算要存储的值i.(和i + 1没有可能在写入之后延迟的副作用.)该依赖性强加了严格的排序:必须在写入开始之前完成读取.

另一方面,这个:

a[i] = (i=1);
Run Code Online (Sandbox Code Playgroud)

有未定义的行为.子表达式a[i] 读取i,子表达式i=1 写入i.但是i写入存储的值不依赖于i左侧读取的评估,因此未定义读取和写入的顺序."要存储的价值"是1; 读ia[i]并不能确定该值.

我怀疑这种混淆是为什么2011年修订的ISO C标准(草案形式为N1570)重新措辞了该部分.该标准仍然具有序列点的概念,但6.5p2现在说:

如果相对于对同一标量对象的不同副作用或使用相同标量对象的值进行值计算,对标量对象的副作用未被排序,则行为未定义.如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的.

第1段明确说明了C99中隐含的假设:

在运算符的结果的值计算之前,对运算符的操作数的值计算进行排序.

第5.1.2.3节第2节解释了之前顺序和关系之后顺序.