14

C99 §6.5表达式

(1) 表达式是一系列运算符和操作数,用于指定值的计算,或指定对象或函数,或产生副作用,或执行它们的组合。

(2) 在前一个和下一个序列点之间,对象的存储值最多只能通过表达式的评估修改一次。72)此外,应仅读取先验值以确定要存储的值。73)

带脚注

72) 浮点状态标志不是对象,可以在表达式中多次设置。

73) 本段呈现未定义的语句表达式,例如

    i = ++i + 1;
    a[i++] = i;

同时允许

    i = i + 1;
    a[i] = i;

其中 C11 §6.5 更改为((1) 的文本有一个附录):

(1) […] 运算符的操作数的值计算在运算符结果的值计算之前排序。

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

其中 C11 中的脚注 84 与 C99 中的脚注 73 相同。

我有点困惑...我将 C11 (2) 读为“[...] (对同一标量对象的不同副作用)或(使用同一标量对象的值的值计算)[...]”,这似乎甚至不允许foo = ++i(有一个副作用,我们根据更改的对象使用一个值)。不过,我不是母语人士,所以如果有人能告诉我应该如何“解析”这句话,那就太好了。我了解C99,但我不太了解C11的措辞。

无论如何,实际的问题是:这是从 C99 到 C11 的变化,还是这些措辞是等价的?如果是这样,为什么它被改变了?如果没有,有人可以举一个表达式的例子,它在 C99 中是 UB,但在 C11 中不是,反之亦然?

4

4 回答 4

5

C11(以及 C++11)完全修改了排序的措辞,因为 C11 现在有线程,它必须解释访问相同数据的线程之间的排序意味着什么。该委员会的目的是在只有一个执行线程的情况下使事情向后兼容 C99。

我们来看看C99版本:

  1. 在上一个和下一个序列点之间

  2. 一个东西

  3. 应有

  4. 它的存储值最多修改一次

  5. 通过表达式的评估。

与新文本相比

如果有副作用

4的不同terminolgie,修改存储值

一个标量对象

对 2 中先前措辞的限制。新文本仅说明了有关标量对象的内容

相对于任何一个都是无序的

unsequenced 是 1. 中概念的概括。两个语句由一个序列点分隔。想想两个线程在不使用锁或类似的东西的情况下修改相同的数据。

同一个标量对象的不同副作用

对象只允许修改一次

或使用同一标量对象的值进行值计算,

或者读取值可能不会同时出现在修改中

行为未定义。

3. 中的“shall”含蓄地说。如果不满足,所有“应”都将导致 UB。

于 2014-01-11T20:07:15.067 回答
3

我有点困惑...我将 C11 (2) 读为“[...] (对同一标量对象的不同副作用)或(使用同一标量对象的值的值计算)[...]”,这似乎甚至不允许foo = ++i(有一个副作用,我们根据更改的对象使用一个值)。

如果您仔细阅读标准报价

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

那么你会发现你的措辞应该是:

如果标量对象的副作用相对于(同一标量对象的不同副作用)或(使用相同标量对象的值的值计算)是无序的。

这意味着这foo = ++i是一个已定义的语句。确实存在 on i(on fooalso) 的副作用,但对于 object 来说,这里没有什么是unsequencedi的。

于 2014-01-11T19:56:12.317 回答
2

这是对问题的解释,foo = ++i但不是真正的答案。


前缀增量是根据复合赋值定义的,见 6.5.3/2

表达式++E等价于(E+=1)

对于一般分配,在 6.5.16/3 中有保证

更新左操作数的存储值的副作用是在左操作数和右操作数的值计算之后排序的。操作数的评估是无序的。

所以foo = ++i等价于foo = (i+=1)。内部i+=1要求在i计算之后对 的修改进行排序i+1。表达式的结果值(i+=1)在 6.5.16/3 中指定为:

赋值表达式在赋值后具有左操作数的值,但不是左值。

似乎这需要在修改 之后对 的值计算进行i+=1排序i,而在 C++11 中,这甚至可以明确保证 [expr.ass]/1

在所有情况下,赋值都在左右操作数的值计算之后和赋值表达式的值计算之前进行排序。

(这对我来说更清楚,但我比 C 更了解 C++)

的修改i在 的值计算之前排序i+=1,因此我们没有 UB 访问++iin的值foo = ++i(因为 的左右操作数的值计算foo = x在 的修改之前排序foo)。

于 2014-01-11T19:23:29.633 回答
1

据我了解,

如果标量对象的副作用相对于...使用同一标量对象的值的值计算是无序的

不适用于此处,因为 (1) 指出

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

换句话说,结果被定义为“稍后出现”,即它排序的。

于 2014-01-11T19:54:18.483 回答