自我赋值中的后增量

时间:2015-11-18 15:27:02

标签: c# post-increment pre-increment

我理解i++ and ++i之间的差异,但我不确定为什么我会得到以下结果:

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

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

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

6 个答案:

答案 0 :(得分:33)

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

IL_0000:  nop
IL_0001:  ldc.i4.s    2A
IL_0003:  stloc.0     // c
IL_0004:  ldloc.0     // c

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

IL_0005:  stloc.1
IL_0006:  ldloc.1

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

IL_0007:  ldc.i4.1
IL_0008:  add

这将常数1加到加载的值

IL_0009:  stloc.0     // c

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

IL_000A:  ldloc.1
IL_000B:  stloc.0     // c

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

IL_000C:  ldloc.0     // c
IL_000D:  call        System.Console.WriteLine
IL_0012:  nop
IL_0013:  ret

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

所以你可以从中看到,当c++在之后将变量递增一个返回结果时,在递给变量值之前仍然会发生递增。所以序列更像是这样:

  1. c
  2. 获取价值
  3. 增加后c
  4. 将先前读取的值分配给c
  5. 这应该可以解释为什么你得到那个结果:)

    再添加一个示例,因为在删除后的评论中提到了这一点:

    c = c++ + c;
    

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

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

答案 1 :(得分:19)

根据the MSDN page on C# operators,分配运算符(=)的优先级低于任何主要运算符,例如++xx++。 这意味着在行

c = c++;

首先评估右侧。表达式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

将此与前缀形式

进行比较
c = ++c;

将评估为

// 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

答案 2 :(得分:5)

文档说明后缀状态:

  

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

这意味着当你这样做时:

c = c++;

您实际上正在将42重新分配给c,这就是您看到控制台打印42的原因。但如果你这样做:

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

您会看到它输出43

如果你看一下compiler generates(在调试模式下),你会看到:

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

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

private static void Main(string[] args)
{
    Console.WriteLine(42);
}

答案 3 :(得分:3)

  

...因为这是将变量赋值给自己然后递增值...

不,它不是它的功能。

帖子 -increment运算符会增加变量,会返回 pre -increment运算符会增加变量,会返回 new

所以你的c++会将c增加到43,但会返回42,然后再将其分配给c。

答案 4 :(得分:3)

完全评估赋值右侧的表达式,然后执行赋值。

x <- c(1, 4, 5, 3, 1)
min(which(diff(x) < 0))
x[min(which(diff(x) < 0))]


set.seed(1)
a<-rnorm(10)
min(which(diff(a) < 0)) 

相同
   c = c++;

答案 5 :(得分:3)

我想我看到了原始提问者的想法。他们正在考虑(我认为)后增量意味着在评估整个表达式之后增加变量,例如,该

x = a[i++] + a[j++];   // (0)

相同
{ x = a[i] + a[j] ; i += 1 ; j += 1; }   // (1)

(并且无可否认这些是等效的)和

c = c++;  // (2)

装置

{ c = c ; c +=1 ; } // (3)

那个

x = a[i++] + a[i++];  // (4)

装置

{ x = a[i] + a[i] ; i += 2 ; } // (5)

但事实并非如此。 v++表示立即增加v,但使用旧值作为表达式的值。因此,在(4)的情况下,实际等效的陈述是

{int t0 = a[i] ; i += 1 ; int t1 = a[i] ; i += 1 ; x = t0 + t1 ; } // (6)

正如其他人所说,像(2)和(4)这样的语句在C#(和Java)中已经很好地定义了,但它们在C和C ++中没有很好地定义。

在C和C ++表达式(如(2)和(4)中)更改变量并以其他方式使用它通常是未定义的,这意味着欢迎编译器(就语言法而言)将它们转换为任何方式,例如将资金从您的银行帐户转移到编译器编写者。