自我赋值中的后增量

Siy*_*ual 35 c# pre-increment post-increment

我理解它们之间的区别i++ and ++i,但我不太清楚为什么我得到以下结果:

static void Main(string[] args)
{
    int c = 42;
    c = c++;
    Console.WriteLine(c);   //Output: 42
}
Run Code Online (Sandbox Code Playgroud)

在上面的代码中,因为这是将变量赋值给自己然后递增值,我希望结果是43.然而,它正在回归42.使用时也得到相同的结果c = c--;.

我意识到我可以简单地使用c++;并完成它,但我更好奇它为什么表现得如此.谁能解释一下这里发生了什么?

pok*_*oke 34

我们来看看中间语言代码:

IL_0000:  nop
IL_0001:  ldc.i4.s    2A
IL_0003:  stloc.0     // c
IL_0004:  ldloc.0     // c
Run Code Online (Sandbox Code Playgroud)

这会将常量整数加载42到堆栈中,然后将其存储到变量中c,并立即将其加载到堆栈中.

IL_0005:  stloc.1
IL_0006:  ldloc.1
Run Code Online (Sandbox Code Playgroud)

这会将值复制到另一个寄存器中,并再次加载它.

IL_0007:  ldc.i4.1
IL_0008:  add
Run Code Online (Sandbox Code Playgroud)

这会将常量1添加到加载的值

IL_0009:  stloc.0     // c
Run Code Online (Sandbox Code Playgroud)

...并将结果(43)存储到变量中c.

IL_000A:  ldloc.1
IL_000B:  stloc.0     // c
Run Code Online (Sandbox Code Playgroud)

然后加载另一个寄存器的值(仍然是42!)并存储到变量中c.

IL_000C:  ldloc.0     // c
IL_000D:  call        System.Console.WriteLine
IL_0012:  nop
IL_0013:  ret
Run Code Online (Sandbox Code Playgroud)

然后从变量加载值(42)并打印.


因此,您可以从中看到,虽然在返回结果后将c++变量递增1,但在将值赋给变量之前仍然会发生递增.所以序列更像是这样的:

  1. 从中获取价值 c
  2. 后递增 c
  3. 将先前读取的值分配给 c

这应该解释为什么你得到那个结果:)


要添加另一个示例,因为在删除后的注释中提到了这一点:

c = c++ + c;
Run Code Online (Sandbox Code Playgroud)

这非常相似:假设初始值为2,则首先评估加法的左侧.因此,从变量(2)读取值,然后c递增(c变为3).然后评估添加的右侧.c读取的值(现在为3).然后进行加法(2 + 3),并将结果(5)分配给变量.


从中可以看出,您应该避免在正常表达式中混合递增和递减操作.虽然行为定义非常明确且具有绝对意义(如上所示),但有时仍难以绕过它.特别是当您将某些内容分配给在表达式中递增的同一变量时,这会很快变得混乱.所以你自己和别人一个忙,并避免增加/减少操作,当他们不完全靠自己:)

  • 它确实递增了实变量,但它在将(旧)值赋给赋值语句中的变量之前这样做了.因此,后增量的效果会立即被赋值覆盖. (11认同)

Com*_*hip 20

根据C#运算符上的MSDN页面,赋值运算符(=)的优先级低于任何主运算符,例如++xx++.这意味着在线

c = c++;
Run Code Online (Sandbox Code Playgroud)

首先评估右侧.表达c++增量c到43,然后返回原始值42作为结果,其用于分配.

作为您链接到州的文档,

[第二种形式是]后缀增量操作.操作的结果是操作数增加之前的值.

换句话说,您的代码相当于

// Evaluate the right hand side:
int incrementResult = c;   // Store the original value, int incrementResult = 42
c = c + 1;                 // Increment c, i.e. c = 43

// Perform the assignment:
c = incrementResult;       // Assign c to the "result of the operation", i.e. c = 42
Run Code Online (Sandbox Code Playgroud)

将此与前缀表单进行比较

c = ++c;
Run Code Online (Sandbox Code Playgroud)

哪个会评估为

// Evaluate the right hand side:
c = c + 1;                 // Increment c, i.e. c = 43
int incrementResult = c;   // Store the new value, i.e. int incrementResult = 43

// Perform the assignment:
c = incrementResult;       // Assign c to the "result of the operation", i.e. c = 43
Run Code Online (Sandbox Code Playgroud)

  • 原因是事情发生的顺序。递增是表达式的一部分,因此表达式 `c++` 会将 c 递增到 43 并返回 42,但这两种情况都发生在赋值回 c 之前。很可能(除了任何编译时优化),c 首先变为 43,然后被覆盖以再次变为 42。您的代码示例很好地解释了这一点,但重点不是。强调的位只是说表达式将返回 42 而不是 43。OP 知道这一点,但期望在赋值之后进行递增。 (2认同)

Yuv*_*kov 5

文档说明后缀状态:

操作的结果是操作数增加之前的值.

这意味着当你这样做时:

c = c++;
Run Code Online (Sandbox Code Playgroud)

你实际上是重新分配42c,这就是为什么你看到控制台打印42.但如果你这样做:

static void Main(string[] args)
{
    int c = 42;
    c++;
    Console.WriteLine(c);  
}
Run Code Online (Sandbox Code Playgroud)

你会看到它输出43.

如果查看编译器生成的内容(在调试模式下),您将看到:

private static void Main(string[] args)
{
    int num = 42;
    int num2 = num;
    num = num2 + 1;
    num = num2;
    Console.WriteLine(num);
}
Run Code Online (Sandbox Code Playgroud)

这更清楚地显示了覆盖.如果查看发布模式,您将看到编译器优化整个调用:

private static void Main(string[] args)
{
    Console.WriteLine(42);
}
Run Code Online (Sandbox Code Playgroud)

  • @Siyual它首先评估右侧,它增加`c`,但随后立即向它'42`,覆盖增量操作. (2认同)