序列点和副作用:C11的安静变化?
C99§6.5 表达式
(1)表达式是一系列运算符和操作数,它们指定计算值,指定对象或函数,或者生成副作用,或执行其组合。
(2)在上一个和下一个序列点之间,一个对象的存储值最多只能通过一个表达式的评估修改一次.72)此外,先验值应该是只读的,以确定要存储的值.73)
与脚注
72)浮点状态标志不是一个对象,可以在表达式中多次设置。
这段描述了未定义的语句表达式,例如
i = ++i + 1;
a[i++] = i;
同时允许
i = i + 1;
a[i] = i;
C11§6.5变更为(附件(1)的内容):
(1)[...]运算符操作数的值计算在运算符结果值计算之前排序。
(2)如果对标量对象的副作用不是相对于同一标量对象的不同副作用或使用相同标量对象的值进行值计算,则行为是未定义的。 如果一个表达式的子表达式有多个可允许的排序,那么如果这种无序的副作用发生在任何顺序中,则行为是不确定的。
C11中的脚注84与C99中的73相同。
我有点困惑......我把C11(2)看作是“[对同一标量对象的不同副作用]或(使用相同标量对象值的值计算)[......]”甚至不允许foo = ++i
(有一个副作用,我们使用一个值取决于改变的对象)。 不过,我不是母语的人,所以如果能告诉我这句话应该如何“解析”,那将会很好。 我了解C99,但我不太了解C11的措辞。
无论如何,真正的问题是:这是从C99到C11的变化,还是这些措辞是相同的? 如果是这样,为什么它被改变了? 如果不是的话,有人可以给出一个在C99中是UB但在C11中不是的表达的例子,反之亦然?
C11(以及C ++ 11)已经完全重写了测序的措辞,因为C11现在拥有线程,并且必须解释访问相同数据的线程之间的排序顺序。 委员会的意图是保持向后兼容C99,因为只有一个执行线程。
让我们来看看C99版本:
在前一个和下一个序列点之间
一个东西
应该有
其储值最多修改一次
通过评估一个表达。
与新文本相比
如果有副作用
不同的terminolgie为4,修改存储的值
一个标量对象
新的文本只是说了一些关于标量对象的东西
相对于其中任何一个都是不确定的
不确定的是概念的概括1.两个陈述由一个序列点分开。 可以考虑修改相同数据而不使用锁或类似的两个线程。
对同一个标量对象有不同的副作用
该对象只允许修改一次
或使用相同标量对象的值进行值计算,
或者读取的值可能不会与修改同时出现
行为是不确定的。
3中的“应该”隐含地这样说。 如果他们没有履行,所有“应该”导致UB。
我有点困惑......我把C11(2)看作是“[对同一标量对象的不同副作用]或(使用相同标量对象值的值计算)[......]”甚至不允许foo = ++i
(有一个副作用,我们使用一个值取决于改变的对象)。
如果你仔细阅读标准报价
如果对标量对象的副作用与相同标量对象的不同副作用或使用相同标量对象的值进行值计算相反,则行为未定义。 如果一个表达式的子表达式有多个可允许的排序,那么如果这种无序的副作用发生在任何顺序中,则行为是不确定的。
那么你会发现你的措词应该是:
如果对标量对象的副作用相对于 ( 相同标量对象的不同副作用)或(使用相同标量对象的值进行值计算)不相关。
这意味着foo = ++i
是一个定义的语句。 这是真的,有一个副作用, i
(对foo
也有),但没有在这里未测序的对象i
。
这是foo = ++i
的解释,但不是真正的问题答案。
前缀增量是根据化合物分配定义的,见6.5.3 / 2
表达式++E
相当于(E+=1)
一般来说,在6.5.16 / 3中有保证
更新左操作数的存储值的副作用在左和右操作数的值计算之后被排序。 操作数的评估是不确定的。
所以foo = ++i
相当于foo = (i+=1)
。 内部i+=1
需要在计算i+1
之后对i
进行修改。 表达式(i+=1)
的结果值在6.5.16 / 3中指定为:
赋值表达式具有赋值后左操作数的值,但不是左值。
它好像这需要的值计算i+=1
的修改后待测序i
,并且在C ++ 11中,这甚至保证明确[expr.ass] / 1
在所有情况下,赋值都在左右操作数的值计算之后,赋值表达式的值计算之前进行排序。
(这对我来说很清楚,但我知道C ++远比C好)
的修改i
的值计算之前进行测序i+=1
,所以我们没有UB访问的值++i
在foo = ++i
(作为左和右操作数的值计算foo = x
在修改foo
之前进行排序)。