简体   繁体   English

C11表达式中的赋值运算符排序

[英]Assignment operator sequencing in C11 expressions

Introduction 介绍

The C11 standard (ISO/IEC 9899:2011) has introduced a new definition of side effect sequencing within an expression ( see related question ). C11标准(ISO / IEC 9899:2011)在表达式中引入了新的副作用测序定义( 参见相关问题 )。 The sequence point concept has been complemented with sequenced before and sequenced after relations which are now the basis for all definitions. 序列点概念已经关系之前进行了序列补充,并且关系之后 进行了 排序,这些关系现在是所有定义的基础。

Section 6.5 "Expressions", point 2 says: 第6.5节“表达式”,第2点说:

If a side effect on a scalar object is unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object, the behavior is undefined. 如果相对于对同一标量对象的不同副作用或使用相同标量对象的值进行值计算,对标量对象的副作用未被排序,则行为未定义。 If there are multiple allowable orderings of the subexpressions of an expression, the behavior is undefined if such an unsequenced side effect occurs in any of the orderings. 如果表达式的子表达式有多个允许的排序,则如果在任何排序中发生这种未测序的副作用,则行为是不确定的。

Later on, section 6.5.16 "Assignment operators", point 3 states: 稍后,第6.5.16节“分配操作员”,第3点指出:

The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. 在左右操作数的值计算之后,对更新左操作数的存储值的副作用进行排序。 The evaluations of the operands are unsequenced. 对操作数的评估是不确定的。

Problem 问题

The first quoted paragraph (6.5/2) is supported by two examples (same as in the C99 standard): 第一个引用的段落(6.5 / 2)由两个例子支持(与C99标准相同):

First example 第一个例子

a[i++] = i;  //! undefined
a[i] = i;    //  allowed

This can be easily explained with the definitions: 这可以通过以下定义轻松解释:

  1. If a side effect on a scalar object is unsequenced relative to (...) a value computation using the value of the same scalar object, the behavior is undefined. 如果相对于(...)使用相同标量对象的值进行值计算,标量对象的副作用未被排序,则行为未定义。 (6.5/2), (6.5 / 2),
  2. The evaluations of the operands are unsequenced. 对操作数的评估是不确定的。 [within an assignment] (6.5.16/3). [在作业内](6.5.16 / 3)。

So, the side effect of i++ (LHS) is unsequenced with i (RHS), which gives undefined behaviour. 因此, i++ (LHS)的副作用与i (RHS)无关,它给出了未定义的行为。

Second example 第二个例子

i = ++i + 1; //! undefined
i = i + 1;   //  allowed

This code, however, seems to result in a defined behaviour in both given cases as: 但是,此代码似乎在两种情况下都会导致定义的行为:

  1. the side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. 在左右操作数的值计算之后,对更新左操作数的存储值的副作用进行排序。

So, the execution of ++i + 1 shall precede the side effect of updating i , which means that there is not a side effect on a scalar object unsequenced relative to either a different side effect on the same scalar object or a value computation using the value of the same scalar object. 因此, ++i + 1的执行应先于更新i的副作用,这意味着相对于对同一标量对象的不同副作用或使用的值计算,对未标测的标量对象没有副作用相同标量对象的值。

Question

It is easy to explain these examples with the terms and definitions presented by the C99 standard ( see related question ). 使用C99标准提出的术语和定义很容易解释这些例子( 参见相关问题 )。 But why is i = ++i + 1 undefined according to C11's terminology? 但根据C11的术语,为什么i = ++i + 1未定义?

Update 更新

I am changing my answer here, this is not well defined in C11 although it is in C++11. 我在这里改变我的答案,虽然它是在C ++ 11中,但在C11中没有很好地定义。 The key here is that the result of ++i is not an lvalue and therefore does not require an lvalue-to-rvalue conversion after ++i is evaluated and so we can not be assured that the result of ++i will be read afterwards. 这里的关键是++i的结果不是左值,因此在评估++i之后不需要进行左值到右值的转换,因此我们无法保证将读取++i的结果然后。 Which is different than C++ and so the defect report I originally linked to hinges on this critical fact: 这与C ++不同,因此我最初链接的缺陷报告取决于这个关键事实:

[...] the lvalue expression ++i and then do an lvalue-to-rvalue conversion on the result. [...]左值表达式++ i然后对结果进行左值到右值的转换。 guarantees that the incrementation side-effect is sequenced before the computation of the addition operation[...] 保证在计算加法运算之前对增量副作用进行排序[...]

we can see this by going to the C11 draft standard section 6.5.3.1 Prefix increment and decrement operators which says: 我们可以通过转到C11草案标准部分6.5.3.1 前缀增量和减量运算符来看到这一点:

[...]The expression ++E is equivalent to (E+=1).[...] [...]表达式++ E相当于(E + = 1)。[...]

and then section 6.5.16 Assignment operators which says ( emphasis mine going forward ): 然后是6.5.16 分配操作员强调我的前进 ):

An assignment operator stores a value in the object designated by the left operand. 赋值运算符将值存储在左操作数指定的对象中。 An assignment expression has the value of the left operand after the assignment, 111 but is not an lvalue .[...] 赋值表达式在赋值后具有左操作数的值, 111 但不是左值 。[...]

and footnote 111 says: 和脚注111说:

The implementation is permitted to read the object to determine the value but is not required to, even when the object has volatile-qualified type. 允许实现读取对象以确定值,但不需要,即使对象具有volatile限定类型。

There is no requirement to read the object to determine it's value even if it is volatile. 即使它是易失性的,也不需要读取对象来确定它的值。

Original Answer 原始答案

As far as I can tell this is actually well defined and this example was removed from the C++ draft standard which uses similar language. 据我所知,这实际上是定义良好的,这个例子已从使用类似语言的C ++草案标准中删除。 We can see this in 637. Sequencing rules and example disagree which says: 我们可以在637看到这一点。 排序规则和示例不同意说:

the following expression is still listed as an example of undefined behavior: 以下表达式仍作为未定义行为的示例列出:

 i = ++i + 1; 

However, it appears that the new sequencing rules make this expression well-defined: 但是,似乎新的排序规则使这个表达式定义明确:

and the resolution was to strike the prefix example and use the postfix example instead which is clearly undefined: 并且解决方案是打击前缀示例并使用后缀示例,而这显然是未定义的:

Change the example in 1.9 [intro.execution] paragraph 16 as follows: 更改1.9 [intro.execution]第16段中的示例,如下所示:

i = ++i i++ + 1; i = ++ i i ++ + 1; // the behavior is undefined //行为未定义

The standard stipulates for assignment (6.5.16) as you are citing correctly 在您正确引用时,标准规定了分配(6.5.16)

The side effect of updating the stored value of the left operand is sequenced after the value computations of the left and right operands. 在左右操作数的值计算之后,对更新左操作数的存储值的副作用进行排序。

(The increment operator is not different, it is just an assignment in disguise) (增量运算符没有区别,它只是伪装的赋值)

This means that there are two value computations (left and right) and the side effect of the assignment is then sequenced after these. 这意味着有两个值计算(左和右),然后在这些值之后对赋值的副作用进行排序。 But it is only sequenced against the value computations, not against the side effects that these may have. 但它仅针对价值计算进行排序,而不是针对这些可能产生的副作用。 So at the end we are faced with two side effects (of the = operator and the ++ operator) that are not sequence with respect to each other. 所以最后我们面临着两个副作用( =运算符和++运算符),它们彼此之间没有顺序。

But why is i = ++i + 1 undefined according to C11's terminology? 但根据C11的术语,为什么i = ++i + 1未定义?

C11 says that the side effect on left i is sequenced but not the value computations (evaluations) of left and right i . C11说,在留下的副作用, i被测序但不是左边和右边的值计算(评估) i
It is obvious that the side effect on LHS will take place after the evaluation of expressions on LHS and RHS. 很明显,LHS的副作用将在评估LHS和RHS的表达后发生。
To explain this a better example could be 为了解释这个,可以有一个更好的例子

int i = 1;
i = i++ + 3;

(First let's assume that this example will not invoke UB). (首先让我们假设这个例子不会调用UB)。 Now the final value of i could be 4 or 2 . 现在i的最终值可能是42
Case 1 . 案例1
Left i is fetched and then it is incremented and 3 is added to it and finally 4 is assigned to i . 左边的i被取出然后它被递增并且3被添加到它,最后4被分配给i
Case 2 . 案例2
Left i is fetched and then 3 is added to it and then 4 is assigned to i and finally i is incremented. 左边是i ,然后将3添加到它,然后将4分配给i ,最后i递增。 In this case the final value of i is 2 . 在这种情况下, i的最终值为2
Although the side effect on left i is sequenced the final value stored to i is not defined, ie it is not necessarily by the assignment and hence the side effect on i is unsequenced. 尽管对左i的副作用是有序的,但是没有定义存储到i的最终值,即它不一定是由赋值,因此对i的副作用是未定序的。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM