在C ++中是++ x%= 10吗?

在浏览某个项目的代码时,我碰到以下声明:

++x %= 10; 

这个陈述是否在C ++中有明确的定义,还是属于与C ++相同的类别

 a[i] = i++ 

按照C ++ 11 1.9 Program execution /15

除了注意到的地方之外,对个别操作符和个别expression式的操作数的评估是不确定的。

如果对标量对象的副作用相对于同一个标量对象的另一个副作用或者使用相同标量对象的值进行值计算而言是不确定的,则行为是不确定的。

在这种情况下,我认为++x是一个副作用, x %= 10是一个值计算,所以你会认为这将是未定义的行为。 然而, 转让部分( 5.17 /1 )有这个说法(我的大胆):

在任何情况下,赋值都是在左右操作数的值计算之后,赋值expression式的值计算之前进行sorting的。

因此,这意味着在分配之前和分配的结果可用之前,双方都是sorting的。 而且由于标准还规定x OP = yx = x OP y x OP = y是相同的( 5.17 /7 ),但是x只被评估一次,结果certificate这明确定义的行为,因为它相当于:

 ++x = Q % 10; // where Q is the value from ++x, not evaluated again. 

接下来唯一的问题是分配的哪一方面因为没有被sorting而被评估。 不过,在这种情况下,我不认为这很重要,因为这两者都会产生同样的效果:

 ++x = Q % 10; // LHS evaluated first Q = ++x % 10; // RHS evaluated first 

现在,这是阅读的标准。 虽然我在解码复杂文档方面有相当多的经验,但是可能有一些我错过了 – 我不这么认为,因为我们都在这里进行了热烈的讨论,以达到这个目的:-)我认为我们已经build立了相关的部分。

但是,不pipe是否定义好,体面的编码人员都不应该这样写代码。 自从PDP minis的低内存/小存储时间以来,已经有很长一段时间了,现在是我们编写代码的时候了

如果你想增加,然后采取模,使用x = (x + 1) % 10 ,如果只是为了更容易理解下一个可怜的乔读这个代码。

TL; DR :定义良好,因为x在赋值之前保证递增。


一些C ++ 11标准

[intro.execution] / 15:

除了注意到的地方之外,对个别操作符和个别expression式的操作数的评估是不确定的。

但是,[expr.ass] / 1确实logging了一些内容:

在任何情况下,赋值都是在左右操作数的值计算之后,赋值expression式的值计算之前进行sorting的

所以这确实构成了第一个引号的例外。 而且,如[expr.pre.incr] 1所述++x等价于x += 1 ,这也被上面的引用覆盖:赋值在计算之前被sorting。 因此, 对于++x ,增量在值计算之前被sorting。

考虑到这一点,不难看出,在++x %= 10 ,增量是在赋值之前完成的。
所以在分配副作用之前对增量副作用进行sorting,从而对所有涉及的副作用进行sorting。

换句话说,该标准规定了以下顺序:

  • ++x10被评估 – 其顺序是不确定的,但10只是一个文字,所以在这里没有关系。
    • 评估++x
      • 首先, x的值递增。
      • 然后值计算完成,我们得到一个左值参考x
  • 分配完成。 x的更新值取模10并赋值给x
  • 分配的值计算可以遵循,在分配之后明确地sorting。

于是

如果对标量对象的副作用相对于同一个标量对象的另一个副作用或者使用相同标量对象的值进行值计算而言是不确定的,则行为是不确定的。

并不适用于副作用和价值计算的顺序


1不是[expr.post.incr]这将是后缀增量!

我们来看一元增量算子:

5.3.2递增和递减[expr.pre.incr]

1前缀++的操作数通过加1来修改,如果是bool (这个用法不推荐),则设置为true 。 操作数应该是一个可修改的左值。 操作数的types应为算术types或指向完全定义的对象types的指针。 结果是更新的操作数; 它是一个左值 ,如果操作数是位域,则它是一个位域。 如果x不是booltypes,则expression式++x等价于x+=1
[…]

因此,与一元操作符有关的所有评估和副作用都被安排在其值之前,因此不会造成严重破坏。

剩下的就是在那个左值上评估%= 10 。 只有评估常量可能是并发的(不可能造成任何伤害),其余的都是严格按照顺序排列。

我会提供一个替代的答案,而不用引用这本好书,因为我认为稍微重写一下就显而易见了。

 ++x %= 10; // as stated x += 1 %= 10; // re-write the 'sugared' ++x 

这在我眼中已经足够清楚了。 我们知道,赋值的结果(如果我们真的想要的话,仍然是“加糖的” +=减less到)本身就是一个左值,所以毫无疑问,通过进一步的减less,expression式是:

 (x = x+1) %= 10 // += -> =1+ x = (x+1) % 10 // assignment returns reference to incremented x 

在expression式中

 ++x %= 10; 

最容易混淆的部分是x在两个序列点之间进行两次修改,一次是前缀++ ,一次是结果赋值。 这让人觉得上面的expression式调用了未定义的行为,就像我们在旧C ++中学到的一样

在前一个和下一个序列点之间,一个标量对象应该通过评估一个expression式来最多修改其存储值。

在C ++ 11中,规则是不同的(这是关于序列而不是序列点 !):

如果对标量对象的副作用相对于同一个标量对象的另一个副作用或者使用相同标量对象的值进行值计算而言是不确定的,则行为是不确定的。

因为我们已经知道++x是上面的expression式,所以只会评估一次(并且会给出一个左值),因为

C ++ 11:5.17

formsE1 op = E2的expression式的行为相当于E1 = E1 op E2 除了E1仅被评估一次

并且还知道对运算符++x10求值将在按照标准计算%=运算符的结果之前进行:

运算符操作数的值计算在运算符结果的值计算之前被sorting。

结论:

++x只会在给出一个左值的情况下评估一次,并且只有在执行了%=操作之后。 这意味着对x修改都被sorting,并且上面的expression式被很好地定义。

那么,这是定义 。 由于未定义的行为:

§1.9/ 15:
如果对标量对象的副作用相对于同一个标量对象的另一个副作用或者使用相同标量对象的值进行值计算而言是不确定的,则行为是不确定的。

这里有两个无法确定的副作用。

给定expression式++x %= 10; 从左向右移动,我们有:

  1. 值计算 (x+1)
  2. 修改一个对象= ,如x = x + 1 ),例如每个§1.9/ 12的副作用
  3. 同一个标量对象 (' x ')上有一个不确定sorting的操作( %= ),它本身同时具有一个值计算(' % ')和一个对象修改副作用 (' = ')( 同上1,2 ) 。

全expression式中的两个子expression式是相对于彼此不相关的 。 虽然我们从左到右地开始阅读,但是这些内容是不确定的 ,所以从第1.9 / 13页明显没有部分顺序的救助:

给定任何两个评估AB ,如果AB之前被sorting,那么A的执行应该在B的执行之前。 如果AB之前未被测序, BA之前未被测序,则AB不被测序。

那么,UDB。

前缀增量(++ x)的模式赋值(%=)的优先级最高。 声明:++ x%= 10; 可以表示为:

 ++x; x%= 10;