在序列点方面的前增量与后增量

时间:2017-03-24 10:54:22

标签: c++ c++14 language-lawyer increment undefined-behavior

this answer中,有一些明确定义和未定义的表达式的例子。我对其中两个特别感兴趣:

(6) i = i++ + 1;    // Undefined Behaviour
(7) i = ++i + 1;    // Well-defined Behaviour

这意味着在序列点和明确/未指定/未定义的行为方面,前增量和后增量之间存在差异,但我不明白这种差异来自何处。

在标准草案( N4618 )中,有一个代码示例([intro.execution],第18页)

  

i = i++ + 1; // the value of i is incremented

     

i = i++ + i; // the behavior is undefined

据我了解,这意味着表达式i = i++ + 1应该定义明确,变量i的值应该增加1作为此表达式的结果。但是,在MSVS 2015中运行的此代码会将i增加2

那么,表达式i = i++ + 1会发生什么?是定义明确,未定义,实现定义还是未指定的行为?如原始答案所述,在序列点和UB方面,这个和类似表达式的前增量和后增量之间是否存在任何差异?为什么Visual Studio会显示与标准中写的不同的行为?

请注意,我主要对现代c ++(14/17)感兴趣。

2 个答案:

答案 0 :(得分:1)

表达式i = i++ + 1会发生什么?它是定义明确,未定义,实现定义还是未指定的行为?

这个确切的例子在标准中给出,我们有多幸运?

  

N4296 1.9.15 [intro.execution]

     

i = i++ + 1; // the behavior is undefined

当然,我们也想知道为什么。以下标准引用似乎与此相关:

  

N4296 1.9.15 [intro.execution]

     

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

这告诉我们总和将在赋值之前发生(呃,它怎么知道要分配什么!),但它并不保证增量将在赋值之前或之后发生,现在我们在浑水......

  

N4296 1.9.15 [intro.execution]

     

[...]如果标量对象的副作用相对于其中任何一个都没有排序   对同一标量对象或值计算的另一个副作用   使用相同标量对象的值,它们不是   可能并发(1.10),行为未定义。 [...]

赋值运算符对i的值有副作用,这意味着我们在同一个标​​量对象上有两个副作用(另一个是由i++执行的赋值),这些副作用是未排序的,这是未定义的。

为什么Visual Studio会显示与标准中写的不同的行为?

没有。该标准表示它是未定义的,这意味着它可以做任何事情,从你想要的东西到完全不同的东西,只是碰巧这是由编译器吐出来的行为!

答案 1 :(得分:0)

  

i = ++i + 1; // Well-defined Behaviour

     

这意味着在序列点和明确/未指定/未定义的行为方面,preincrement和postincrement之间存在差异,但我不明白这种差异来自何处。

我认为帖子在几个方面是不正确的。引用与他们相同的部分,强调我的:

  

C ++ 11 1.9 / 15
  一个操作数的值计算   在运算符结果的值计算之前对运算符进行排序。如果对标量有副作用   对于相同标量对象或值计算的另一个副作用,对象未被排序   使用相同标量对象的值,行为未定义

然后是赋值运算符:

  
    

C ++ 11 5.17
    在所有情况下,赋值在右和左操作数的值计算之后,以及赋值表达式的值计算之前排序。

  

值得注意的是,值计算或左右操作数未按顺序排列。 (这已在C11 1)中明确说明,否则其文本与C ++ 11完全相同。)

意味着在表达式i = ++i + 1;中,++i的副作用与左操作数i值计算无关。因此,根据1.9 / 15,它是未定义的行为。 UB与任务副作用毫无关系。

对于表达式i = i++ + 1;,赋值的副作用是,根据C ++ 11,在值计算之后,但在表达式的值计算整体之前显式排序。根据5.2.6,i++的值计算不是问题“++表达式的值计算在修改操作数对象之前被排序”。根据后缀++的性质,更新i++的副作用必须在整个表达式的值计算之后排序。据我所知,这是明确定义的行为。

因此,正确的文字应为

(6) i = i++ + 1;    // Well-defined Behaviour
(7) i = ++i + 1;    // Undefined Behaviour

显然在C ++ 11 1.9 / 15 i = i++ + 1; // the behavior is undefined中有一个不正确的例子,该例子已在该标准的更高版本中得到纠正。

注意:对于序列点的措辞改变,这些都没有任何关系!

1) C11 6.5.16 / 3

  

更新存储值的副作用   左操作数在左和右的值计算之后排序   正确的操作数。 操作数的评估未被排除。