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

在C99 6.5中说:

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

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

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

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

“此外,先前的值应该只读以确定要存储的值”部分意味着i = i + 1; 是允许的,虽然它从i读取并修改i

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

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

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

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

这个:

 i = i + 1; 

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

另一方面,这个:

 a[i] = (i=1); 

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

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

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

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

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

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