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

时间:2014-03-24 17:09:56

标签: c expression language-lawyer undefined-behavior c11

简介

C11标准(ISO / IEC 9899:2011)在表达式(see related question)中引入了新的副作用测序定义。 序列点概念已经在之前对进行了排序,并且在关系之后进行了排序,现在是所有定义的基础。

第6.5节“表达”,第2点说:

  

如果相对于不同的副作用,对标量对象的副作用未被排序   在相同的标量对象上或使用相同标量的值进行值计算   对象,行为未定义。如果有多个允许的排序   表达式的子表达式,如果这样一个未经测序的一方,则行为是不确定的   效果发生在任何排序中。

稍后,第6.5.16节“分配操作员”,第3点指出:

  

在左右操作数的值计算之后,对更新左操作数的存储值的副作用进行排序。评估   操作数没有排序。

问题

第一个引用的段落(6.5 / 2)得到两个例子的支持(与C99标准相同):

第一个例子

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

这可以通过定义轻松解释:

  1. 如果对标量对象的副作用相对于(...)使用相同标量对象的值进行计算而未被排序,则行为未定义。(6.5 / 2),
  2. 操作数的评估未被排序。 [在作业内](6.5.16 / 3)。
  3. 因此,i++(LHS)的副作用未使用i(RHS)进行排序,这会产生未定义的行为。

    第二个例子

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

    但是,这段代码似乎在两种情况下都会导致定义的行为:

    1. 在左右操作数的值计算之后,更新左操作数的存储值的副作用。
    2. 所以,执行++i + 1 应该在更新i的副作用之前,这意味着相对于对同一标量对象的不同副作用或使用值的值计算,对于未序列的标量对象没有副作用相同的标量对象。

      问题

      使用C99标准(see related question)提供的术语和定义很容易解释这些示例。但是根据C11的术语,为什么i = ++i + 1未定义?

3 个答案:

答案 0 :(得分:6)

更新

我在这里改变我的答案,虽然它在C ++ 11中,但在C11中没有很好地定义。这里的关键是++i的结果不是左值,因此在评估++i之后不需要左值到右值的转换,因此我们无法确定++i的结果1}}之后会被阅读。这与C ++不同,因此我最初链接的缺陷报告取决于这个关键事实:

  

[...]左值表达式++ i然后对结果进行左值到右值的转换。保证在计算加法运算[...]

之前对递增副作用进行排序

我们可以通过转到C11 draft standard部分6.5.3.1 前缀增量和减少运算符来看到这一点,其中包含:

  

[...]表达式++ E相当于(E + = 1)。[...]

然后是6.5.16 分配运营商部分(强调我的前进):

  

赋值运算符将值存储在左操作数指定的对象中。一个   赋值表达式具有赋值后的左操作数的值, 111 但不是左值。[...]

和脚注111说:

  

允许实现读取对象以确定值,但不需要,即使对象具有volatile限定类型。

即使它是不稳定的,也无需读取对象来确定它的值。

原始答案

据我所知,这实际上定义得很好,这个例子已经从使用类似语言的C ++草案标准中删除了。我们可以在637. Sequencing rules and example disagree 中看到这一点:

  

以下表达式仍被列为未定义行为的示例:

i = ++i + 1;
     

然而,似乎新的测序规则使这个表达式定义明确:

并且解决方案是打击前缀示例并使用后缀示例,而这显然是未定义的:

  

更改1.9 [intro.execution]第16段中的示例,如下所示:

     

i = ++ i i ++ + 1; //行为未定义

答案 1 :(得分:4)

标准规定了分配(6.5.16),因为你正确引用

  

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

(增量运算符没有区别,它只是伪装的赋值)

这意味着有两个值计算(左和右),然后在这些值之后对赋值的副作用进行排序。但它仅针对价值计算进行排序,而不是针对这些可能产生的副作用。因此,最后我们面临着两个副作用(=运算符和++运算符),它们彼此之间没有序列。

答案 2 :(得分:3)

  

但根据C11的术语,为什么i = ++i + 1未定义?

C11表示左侧i的副作用是有序的,但不是左右i的值计算(评估)。
很明显,LHS的副作用将在评估LHS和RHS的表达后发生 为了解释这个,一个更好的例子可能是

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

(首先让我们假设这个例子不会调用UB)。现在i的最终值可以是42 案例1
提取左i,然后递增并添加3,最后将4分配给i
案例2
提取左i,然后将3添加到其中,然后将4分配给i,最后i递增。在这种情况下,i的最终值为2 虽然对左i的副作用是按顺序排列的,但是没有定义存储到i的最终值,即它不一定是赋值,因此对i的副作用未被排序。