为什么C ++ 11中的“i = i ++ + 1”未定义行为? [英] Why is `i = i++ + 1` undefined behavior in C++11?

查看:163
本文介绍了为什么C ++ 11中的“i = i ++ + 1”未定义行为?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在阅读n3290草案的C ++ 11标准(尽可能接近实际的标准文本),我注意到 i = i ++ + 1; 产生未定义的行为。我已经看过类似的问题,但他们是回答较旧的标准(序列点)。新标准引入了表达式和子表达式执行之前/之后的排序关系的概念。


在单线程(1.10)执行的评估之间,序列之前是不对称,传递,成对关系
,其在这些评估中引入
a部分顺序。给定任何两个评估A
和B,如果A在B之前排序,则A的执行将在执行B之前
。如果A在B和B之前没有排序,则
不是在A之前测序,则A和B未测序。 [注意:
执行无序的评估可以重叠。 -end note]
评估A和B是不确定的顺序,当A是
在B之前排序或B在A之前排序,但是未指定
。 [注意:不确定顺序的评估不能重叠,
,但也可以先执行。 -end note]



1.9 14每个值
与完全表达式相关的计算和副作用是
在每个值计算和副作用之前排序



1.9 15除了
注释,单个操作符和$ b的操作数的求值$ b个单独表达式的子表达式无序。 [注:在
中一个在执行
a程序期间被评估多于一次的表达式,未定序和不确定顺序的
评估它的子表达式不需要在不同的
评估中一致地执行。 -end note]
运算符的操作数的值计算在运算符
的结果的值计算之前排序。如果对标量对象的副作用不是相对于对同一标量对象的另一个副作用的
或使用相同标量对象的值的
值计算,则
行为是未定义的。




  [示例:
void f(int,int);
void g(int i,int * v){
i = v [i ++]; //行为未定义
i = 7,i ++,i ++; // i become 9
i = i ++ + 1; //行为未定义
i = i + 1; // i的值增加
f(i = -1,i = -1); // the behavior is undefined
}
-end example]

我理解,它的工作原理:




  • operator = 有两个操作数表达式:参考 i i ++ + 1 ,两者彼此不相关。第二个对 i 有副作用,但第一个似乎在我没有副作用或用于值计算(或参考采用一个值计算相同标量对象的值?它实际上取决于存储在i中的值吗?不这么认为),所以它不是未定义的行为;

  • = 执行在操作数评估之后排序。它对 i 有副作用,但它对两个操作数的顺序都很好,因此不是udefined行为;

  • i ++ + 1 是明显定义的行为。



还是由于其他原因,这行未定义的行为?



PS。标准实际上表示


在运算符结果的值计算之前,运算符的操作数的值计算顺序为


,并没有提及这种情况下的副作用。然而,排序关系仅在表达式求值之间定义,并且求值=值计算+副作用。所以,我不得不假设这个草案在这里不一致,或者假设在这一行,他们意味着评价,而不是价值计算。

编辑:



我想我会在这儿回答,我的困惑的原因:


5 1表达式是一系列操作符和操作数,
指定一个计算。表达式可以导致一个值,并且可以
导致副作用。


因此,操作符的操作数不是子表达式本身。因此,仅对整个 i = i ++ + 1; 的值计算进行排序,并且没有提到标准的副作用测序。这就是为什么它是未定义的原因。



请注意,如果eg。 operator = 对于给定的类型是重载的(因此它将是一个隐含的函数调用),它不会是未定义的行为,对吗?

解决方案

这是未定义的行为,而不是未指定。未定义是指机器允许做任何事情,包括输出一个空程序,随机终止或爆炸。当然,当移植到另一个平台是一个微妙的意外值是一个更可能的结果。



未定义的行为适用于任何情况下,两个副作用应用于同一个标量,相对于彼此排序。在这种情况下,副作用碰巧是相同的(增量 i 从表达式之前的原始值),但是通过标准的字母,它们组合产生UB



副作用不受影响,因为除了?: || &&& ,运算符不定义排序规则C ++ 11§5.15/ 2:


如果计算第二个表达式,则与第一个表达式相关联的每个值计算和副作用


赋值运算符定义了一个特殊的排序规则,第5.17节/ 1:


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


这不帮助 i = i ++ + 1 因为 i ++ 的副作用不是任何值计算的一部分。


I am reading n3290 draft of C++11 standard (as close as I could get to actual standard text), and I noticed that i = i++ + 1; produces undefined behavior. I have seen similar questions before, but they were answered in terms of older standards (Sequence points). New standard introduces instead concept of Sequencing before/after relation between expression and sub-expression executions.

1.9 13 Sequenced before is an asymmetric, transitive, pair-wise relation between evaluations executed by a single thread (1.10), which induces a partial order among those evaluations. Given any two evaluations A and B, if A is sequenced before B, then the execution of A shall precede the execution of B. If A is not sequenced before B and B is not sequenced before A, then A and B are unsequenced. [ Note: The execution of unsequenced evaluations can overlap. —end note ] Evaluations A and B are indeterminately sequenced when either A is sequenced before B or B is sequenced before A, but it is unspecified which. [ Note: Indeterminately sequenced evaluations cannot overlap, but either could be executed first. —end note ]

1.9 14 Every value computation and side effect associated with a full-expression is sequenced before every value computation and side effect associated with the next full-expression to be evaluated.

1.9 15 Except where noted, evaluations of operands of individual operators and of subexpressions of individual expressions are unsequenced. [ Note: In an expression that is evaluated more than once during the execution of a program, unsequenced and indeterminately sequenced evaluations of its subexpressions need not be performed consistently in different evaluations. —end note ] The value computations of the operands of an operator are sequenced before the value computation of the result of the operator. If a side effect on a scalar object is unsequenced relative to either anotherside effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined.

[ Example:
void f(int, int);
void g(int i, int* v) {
i = v[i++]; // the behavior is undefined
i = 7, i++, i++; // i becomes 9
i = i++ + 1; // the behavior is undefined
i = i + 1; // the value of i is incremented
f(i = -1, i = -1); // the behavior is undefined
}
—end example ]

The way I understand it, it works like that:

  • operator= has two operand expressions: taking reference to i and i++ + 1, both unsequnced to one another. The second one has side effect on i, but the first one seems to me not to have side effect or be used in value computation (or is reference taking "a value computation using the value of the same scalar object"? does it actually depend on value stored in i? don't think so), so it's not undefined behavior;
  • operator= execution is sequenced after both operand evaluation. It has side effect on i, but it's well-sequenced in reference to both operands so it's not udefined behavior;
  • i++ + 1 is obviously defined behavior.

Am I wrong about something here? Or is this line undefined behaviour for some other reason?

PS. Standard actually says

The value computations of the operands of an operator are sequenced before the value computation of the result of the operator.

, and it doesn't mention side effects in this context at all. However sequencing relation is defined only between expression evaluations, and evaluation = value computing + side effects. So either I have to assume this draft to be inconsistent here, or assume that in this line they meant evaluation instead of value computation. Or am I wrong here?

EDIT:

I guess I will be answering myself here, but that was the reason for my confusion:

5 1 An expression is a sequence of operators and operands that specifies a computation. An expression can result in a value and can cause side effects.

So operands of operators are not sub-expressions themselves. Therefore only value computation for entire i = i++ + 1; is sequenced, and no mention of side effect sequencing is made by standard. That's the reason why it's undefined.

Note that if eg. operator= was overloaded for given type (so it would be an implicite function call) it wouldn't be undefined behavior, right?

解决方案

It's "undefined behavior," not "unspecified." Undefined means the machine is allowed to do anything including output an empty program, terminate randomly, or explode. Of course, a subtly unexpected value when porting to another platform is a more likely outcome.

Undefined behavior applies to any case where two side effects apply to the same scalar without being sequenced relative to each other. In this case, the side effects happen to be identical (both increment i from its original value before the expression), but by the letter of the standard, they combine to produce UB.

The side effects are unsequenced because aside from ,, ?:, ||, and &&, operators do not define sequencing rules in terms such as C++11 §5.15/2:

If the second expression is evaluated, every value computation and side effect associated with the first expression is sequenced before every value computation and side effect associated with the second expression.

The assignment operators do define a special sequencing rule, §5.17/1:

In all cases, the assignment is sequenced after the value computation of the right and left operands, and before the value computation of the assignment expression.

This does not help i = i ++ + 1 because the side effect of i ++ is not part of any value computation.

这篇关于为什么C ++ 11中的“i = i ++ + 1”未定义行为?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆