在前一个和下一个序列点之间,一个对象应该具有它 通过表达式的评估,最多修改一次存储值。 此外,先前的值应只读以确定该值 存储
什么是“此外,先前的值只能读取以确定要存储的值”是什么意思?在C99中,为什么a[i++] = 1
是未定义的行为?
答案 0 :(得分:8)
a[i++] = 1
(除非其他原因未定义,而不是副作用的排序:超出限制的访问权限,或未初始化的i
)。
你的意思是a[i++] = i
,这是未定义的行为,因为它在与i
相同的序列点之间读取i++
,这会改变它。
“此外,先前值应只读以确定要存储的值”部分表示允许i = i + 1;
,但它从i
读取并修改i
。
另一方面,a[i] = (i=1);
是不允许的,因为尽管只向i
写了一次,但i
的读取并非用于计算存储的值。
答案 1 :(得分:8)
“先前的值应该是只读的,以确定要存储的值”。措辞无疑是违反直觉的;为什么要读取值的目的?
该句的要点是强制要求依赖哪些操作。
我会从Pascal's answer窃取示例。
此:
i = i + 1;
非常好。 i
在同一个表达式中被读取和写入,没有中间序列点,但是没关系,因为在读取完成之后才能进行写入。在完全评估表达式i + 1
及其子表达式i
之前,无法计算要存储的值。 (并且i + 1
没有可能在写入之后延迟的副作用。)该依赖性强制执行严格排序:必须在写入开始之前完成读取。
另一方面,这个:
a[i] = (i=1);
有未定义的行为。子表达式a[i]
读取 i
的值,子表达式i=1
写入 i
的值。但是写入存储在i
中的值不依赖于左侧读取i
的评估,因此未定义读取和写入的顺序。 “要存储的值”是1
; i
中a[i]
的读取并未确定该值。
我怀疑这种混淆是为什么2011年修订的ISO C标准(草案形式为N1570)重新措辞了该部分。该标准仍然具有序列点的概念,但6.5p2现在说:
如果标量对象的副作用相对于其中任何一个都没有排序 对同一个标量对象或值有不同的副作用 使用相同标量对象的值进行计算,行为是 未定义。如果有多个允许的排序 表达式的子表达式,如果这样的话,行为是不确定的 任何顺序都会出现无序的副作用。
第1段明确说明了C99中隐含的假设:
对运算符的操作数的值计算进行排序 在运算符结果的值计算之前。
第5.1.2.3节第2段解释了在之前排序的和之后的排序。