前缀(++ x)和后缀(x ++)操作如何工作?

时间:2011-10-18 18:53:10

标签: c# c++ c sequence-points

有人能告诉我前缀/后缀运算符是如何工作的吗?我一直在网上看很多但没找到任何东西。

从我可以告诉prefex第一个增量,然后进行操作然后分配 Postfix首先执行操作,然后分配然后增加。

但我的代码遇到了一些麻烦:

int x, y;
x = 1;
y = x + x++; // (After operation y = 2)(x=2)

然而,当我这样做时:

y = x++ + x; // (After operation y = 3)(x=2)

我不确定为什么这些操作会有所不同。我有两个问题:

  • 你能解释一下这个区别吗?

  • 这如何适用于其他运营商前缀?

7 个答案:

答案 0 :(得分:60)

这个问题得到了相当多的要求。请注意,每当有人问这个问题时,很多人都会发布错误的答案。很多人对这些操作员的工作方式有不正确的想法,包括编写编程书籍的人,从而教导其他人的谎言。请仔细阅读其他答案。

要正确分析C#行为,请参阅:

What is the difference between i++ and ++i?

对于C ++,在您观察副作用的情况下,任何行为都是正确的行为。 C ++没有定义增量的副作用何时可见。任何两个编译器都可以采用不同的方式。

遵循的一个好规则是不依赖于任何语言中发生副作用的顺序,但肯定在C ++中不依赖它,因为它不可靠。

查看您的具体案例:

int x, y;     
x = 1;     
y = x + x++; 

您报告x和y都是2.这在C#中是正确的。在C#中,正确的行为是:

  • 将y评估为变量
  • 将x评估为值 - 它是1
  • 将x ++评估为值。这会将x计算为变量,然后取其原始值1,然后递增该值,即2,然后将2分配给x,然后生成原始值,即1。
  • 评估1 + 1,即2
  • 将2分配给y。

所以x和y在C#中都是2。

C ++可以做同样的事情,但允许以从右到左的顺序评估添加。也就是说,允许这样做:

  • 将x ++评估为值。这会将x计算为变量,然后取其原始值1,然后递增该值,即2,然后将2分配给x,然后生成原始值,即1。
  • 评估x作为值 - 它是2
  • 评估1 + 2,即3
  • 将y评估为变量
  • 将3分配给y。

C ++也允许这样做:

  • 将x ++评估为值。这将x作为变量进行求值,然后取其原始值1,然后递增该值,这是2 ...步骤中缺失的步骤...然后得到原始值,即1。
  • 将x评估为值 - 它是1
  • 评估1 + 1,即2
  • 将2分配给x - 之前缺少的步骤。
  • 将y评估为变量
  • 将2分配给y。

所以在C ++中,你可以得到y为3或2,这取决于编译器编写者的心血来潮。在C#中,你总是得到y是2.在C ++中,增量的赋值可以随时发生,只要它确实发生。在C#中,增量的赋值必须在计算递增的值之后发生,并且之前使用原始值。 (从执行线程观察时;如果你试图从另一个线程或线程中观察这些东西,所有的赌注都会关闭。)

在你的第二个例子中:

y = x++ + x; 

在C#中,所需的行为是:

  • 将y评估为变量
  • 将x ++评估为值。这会将x计算为变量,然后取其原始值1,然后递增该值,即2,然后将2分配给x,然后生成原始值,即1。
  • 评估x作为值 - 它是2
  • 评估1 + 2,即3
  • 将3分配给y。

因此,C#中的正确答案是y为3,x为2。

同样,C ++可以按任何顺序执行这些步骤。允许C ++:

  • 将x评估为值 - 它是1
  • 将x ++评估为值。这会将x计算为变量,然后取其原始值1,然后递增该值,即2,然后将2分配给x,然后生成原始值,即1。
  • 评估1 + 1,即2
  • 将y评估为变量
  • 将2分配给y。

同样,在C ++中,正确答案是y为2或3,具体取决于编译器编写者的心血来潮。在C#中,正确的答案是y是3。

答案 1 :(得分:28)

  • 在C#中,+的操作数按从左到右的顺序排列。
  • 在C和C ++中,+的操作数的评估顺序未经规范。

对于C#,您的示例的工作方式如下:

 y = x + x++;
     ^ x is 1
         ^ x is increased to 2, but the postfix increment returns the old value (1)
 y = 2

 y = x++ + x;
     ^ x becomes 2, but postfix increment returns the old value (1)
           ^ x is now 2 here
 y = 3

答案 2 :(得分:6)

在这两种情况下,在使用x后应用增量。首先,评估如下: y = 1 + 1(递增到2)

在第二个

y = 1(递增到2)+2。

这就是你得到不同答案的原因。

答案 3 :(得分:6)

在C和C ++中:
输出为未指定

参考 - C ++ 03标准:

第5节:表达式,第4段:

  除非另有说明,否则

&& amp;的特殊规则和||],单个运算符的操作数和各个表达式的子表达式的评估顺序以及副作用发生的顺序是未指定的。

在C99第6.5节中。

  

“运算符和操作数的分组由语法表示.72除了稍后指定的(对于函数调用(),&&,||,?:和逗号运算符),顺序为子表达的评估和副作用发生的顺序都没有说明。“

答案 4 :(得分:4)

表达式x++++x同时包含结果(或值)和副作用

如果我们将讨论限制为整数类型操作数,x++结果就是x的当前值。 副作用是将x增加1.因此,给定代码

x = 0;
y = x++;

结果将是x == 1和y == 0(假设xy是整数类型)。

对于++x结果为1加上当前值x副作用是将x增加1.因此,给定代码

x = 0;
y = ++x;

结果将是x == y == 1.

C和C ++与C#的区别在于评估操作数和应用副作用时。 C#保证表达式中的操作数始终从左到右进行求值。对于所有其他运算符,C和C ++仅保证对&&||?:,逗号和函数调用()运算符进行从左到右的评估。评估操作数的顺序是未指定

类似地,在C#中,x++++x的副作用将在评估表达式后立即应用,而C和C ++仅要求在下一个之前应用副作用< em>序列点

C#的评估规则保证x = x++a = b++ * b++a[i] = i++等表达式定义明确,而C和C ++语言定义明确表示此类表达式导致未定义的行为(任何结果都是可能的)。

答案 5 :(得分:3)

x + x ++和x ++ + x是您不想依赖的病理性副作用病例。 x ++和++ x都增加x,但是在添加x时,评估的顺序是未定义的 - 编译器可以选择首先评估哪个'side'。

答案 6 :(得分:0)

考虑:

y = x + x++;

是否定义了它的行为(它在C和C ++中是未定义的;显然它在C#中定义得很好),无论你想做什么,都必然有更好的表达方式。

如果您正在进行严格的从左到右的评估,那么上述内容可以写成:

y = x * 2;
x ++;

对于知道=*++意味着什么的读者来说,这个含义是明确无误的,并且您的代码的未来维护者不会想要追捕你

如果您不相信编译器生成有效的代码,您可以编写x + xx << 1,但这种不信任通常是错误的。

如果你坚持,你甚至可以写:

y = x++ * 2;

这对我的个人品味来说有点简洁,但它仍然毫不含糊。

如果你想了解其他人的代码(这无疑是程序员花费大量时间做的事情),那么理解复杂的表达式可能很重要。但是当你编写自己的代码时,清晰度比保存键击更重要(或者展示你对运算符优先级图表的了解程度)。