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

本文关键字:何工作 工作 ++x 后缀 前缀 操作 | 更新日期: 2023-09-27 18:11:32

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

据我所知,前缀首先加1,然后做运算,然后赋值。
后缀将先执行操作,然后赋值,然后自增。

但是我的代码有点问题:

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

但是当我这样做的时候:

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

我不知道为什么这些操作会有什么不同。我有两个问题:

  • 你能解释一下这两者的区别吗?

  • 这如何适用于其他操作符前缀?

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

这个问题经常被问到。请注意,每次有人问这个问题,都会有很多人给出错误的答案。很多人对这些运算符的工作方式有不正确的认识,包括那些写编程书的人,他们教别人一些错误的东西。仔细阅读这里的其他答案。

关于c#行为的正确分析,请参见:

i++和++i的区别是什么?

对于c++,任何行为都是正确的行为,在你观察到副作用的情况下。 c++没有定义什么时候递增的副作用是可见的。任何两个编译器都可以做不同的事情。

在任何语言中,都不要依赖于副作用发生的顺序,但是在c++中,当然也不要依赖于,因为它不可靠。

看你的具体情况:

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

你报告x和y都是2。这在c#中是正确的。在c#中正确的行为是:

  • 将y作为变量求值
  • 计算x作为一个值——它是1
  • 将x++作为值计算。它将x作为一个变量计算,然后取其原始值1,然后将该值加1,即2,然后将2赋值给x,然后得到原始值1。
  • 计算1 + 1,即2
  • 将2赋值给y

所以x和y在c#中都是2

c++也可以做同样的事情,但是允许按从右到左的顺序计算加法。也就是说,允许这样做:

  • 将x++作为值计算。它将x作为一个变量计算,然后取其原始值1,然后将该值加1,即2,然后将2赋值给x,然后得到原始值1。
  • 将x作为值计算——它是2
  • 计算1 + 2,即3
  • 将y作为变量求值
  • 将3赋值给y

c++也允许这样做:

  • 将x++作为值计算。它将x作为一个变量求值,然后取其初始值1,然后将该值加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,然后将该值加1,即2,然后将2赋值给x,然后得到原始值1。
  • 将x作为值计算——它是2
  • 计算1 + 2,即3
  • 将3赋值给y

所以c#的正确答案是y = 3 x = 2

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

  • 计算x作为一个值——它是1
  • 将x++作为值计算。它将x作为一个变量计算,然后取其原始值1,然后将该值加1,即2,然后将2赋值给x,然后得到原始值1。
  • 计算1 + 1,即2
  • 将y作为变量求值
  • 将2赋值给y

同样,在c++中,正确的答案是y是2或3,这取决于编译器编写器的心血来潮。在c#中,正确的答案是y = 3

  • 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

在C和c++:
输出为Unspecified

参考- c++ 03标准:

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

除非特别注明[例如:&&和||],单个操作符和单个表达式的子表达式的操作数的求值顺序以及副作用发生的顺序未指定。

在C99第6.5节。

"操作符和操作数的分组由语法指示。72)除后面指定(对于function-call()、&&、||、?:和逗号操作符)外,子表达式的求值顺序和副作用发生的顺序都未指定。"

在这两种情况下,增量都是在使用x之后应用的。在第一种情况下,评估如下:Y = 1 + 1(自增为2)

在第二个

y = 1(自增到2)+ 2.

这就是为什么你得到不同的答案。

表达式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++只要求在下一个序列点之前应用副作用。c#的求值规则保证像x = x++a = b++ * b++a[i] = i++这样的表达式是定义良好的,而C和c++语言的定义明确地说这样的表达式会导致未定义的行为(任何结果都是可能的)。

x + x++和x++ + x是您不想依赖的病理副作用的例子。x++和++ X都增加X,但在增加X时,计算顺序是未定义的-编译器可以选择先计算哪一边。

考虑:

y = x + x++;

它的行为是否被定义(它在C和c++中未定义;显然c#中定义得很好),不管你想做什么,肯定有更好的表达方式。

如果你假设严格的从左到右求值,那么上面的可以写成:

y = x * 2;
x ++;

对于任何知道=*++是什么意思的读者来说,其含义都是清晰而明确的,并且未来的代码维护者也不会试图找到您。

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

如果你坚持的话,你甚至可以这样写:

y = x++ * 2;

这对我个人的口味来说有点简洁,但它仍然是明确的。

如果你想理解别人的代码(不可否认程序员要花很多时间去做这件事),那么理解复杂的表达式是很重要的。但是,当您编写自己的代码时,清晰度比节省击键次数(或展示您对操作符优先级图的了解程度)更重要。