30

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

据我所知,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)

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

  • 你能解释一下区别吗?

  • 这如何应用于其他运算符前缀?

4

7 回答 7

62

这个问题被问了很多。请注意,每次有人问这个问题时,都会有很多人发布错误的答案。很多人对这些运算符的工作方式有错误的想法,包括编写编程书籍并因此向他人传授谎言的人。仔细阅读此处的其他答案。

有关 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,然后将该值递增 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。

于 2011-10-18T18:58:59.733 回答
29
  • 在 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
于 2011-10-18T18:55:23.627 回答
7

在 C 和 C++ 中:
输出为Unspecified

参考 - C++03 标准:

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

除非另有说明 [例如 && 和 || 的特殊规则],单个运算符的操作数和单个表达式的子表达式的求值顺序以及副作用发生的顺序是未指定的。

在 C99 第 6.5 节中。

"运算符和操作数的分组由语法指示。72) 除稍后指定(对于函数调用 ()、&&、||、?: 和逗号运算符)外,子表达式的求值顺序和顺序其中发生的副作用都未指定。”

于 2011-10-18T19:01:46.990 回答
6

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

在第二

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

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

于 2011-10-18T18:57:15.357 回答
4

表达式x++++x既有结果(或值)又有副作用

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

x = 0;
y = x++;

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

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

x = 0;
y = ++x;

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

C 和 C++ 与 C# 的区别在于何时计算操作数以及何时应用副作用。C# 保证表达式中的操作数总是从左到右计算。&&C 和 C++ 仅保证, ||, ?:, 逗号和函数调用运算符的计算从左到右()- 对于所有其他运算符,计算操作数的顺序是未指定的。

x++类似地,在 C# 中,和的副作用将在计算完表达式后立即应用,而 C 和 C++ 只要求在下一个序列点++x之前应用副作用。

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

于 2011-10-18T21:31:36.597 回答
3

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

于 2011-10-18T18:56:37.573 回答
0

考虑:

y = x + x++;

无论其行为是否已定义(在 C 和 C++ 中未定义;显然它在 C# 中已明确定义),无论您尝试做什么,都必然有更好的方式来表达它。

如果您假设严格的从左到右评估,那么上面可以写成:

y = x * 2;
x ++;

对于任何知道 、 和 含义的读者来说,其含义是清晰明确的=*并且++您的代码的未来维护者不会试图追捕您。

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

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

y = x++ * 2;

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

如果您想理解别人的代码(诚然,这是程序员花费大量时间做的事情),那么理解复杂的表达式可能很重要。但是,当您编写自己的代码时,清晰比节省击键(或炫耀您对运算符优先级图表的了解程度)更重要。

于 2011-10-19T04:04:09.120 回答