C#中i=i++问题的分析与备忘

今天在群里有人问到:

int i = 0; 

i = i++; 

Console.WriteLine( i );
以上代码输出的结果是多少?

我很自以为是的回答是:1

可结果为什么不是1呢?先说一下我的分析思路

这段小程序生成的IL代码为:

  .maxstack  3

  .locals init (int32 V_0,

           int32 V_1)

  IL_0000:  ldc.i4.0

  IL_0001:  stloc.0

  IL_0002:  ldloc.0

  IL_0003:  dup

  IL_0004:  ldc.i4.1

  IL_0005:  add

  IL_0006:  stloc.0

  IL_0007:  stloc.1

  IL_0008:  ldloc.1

  IL_0009:  call       void [mscorlib]System.Console::WriteLine(int32)

  IL_000e:  ret

其中dup指令的解释为:duplicate the top value of the stack (拷贝栈顶的值)

本来栈中的值为:0,执行过dup指令后栈中的值就变成:0|0了(|表示栈中各个值间的分隔,这里表示栈中有两个值)

可以看出就因为这个dup指令才导制了最后i的值还是变成了0

那么是不是只要有++操作就会产生dup指令呢?于是我把代码改成以下形式:

int i = 0; 

i++; 

Console.WriteLine( i );

得到的IL代码为:

  .maxstack  2

  .locals init (int32 V_0)

  IL_0000:  ldc.i4.0

  IL_0001:  stloc.0

  IL_0002:  ldloc.0

  IL_0003:  ldc.i4.1

  IL_0004:  add

  IL_0005:  stloc.0

  IL_0006:  ldloc.0

  IL_0007:  call       void [mscorlib]System.Console::WriteLine(int32)

  IL_000c:  ret

可以看到并未出现dup指令,至此我们可以推断当把++之类(i++/++i)的结果值赋予一个变量时才会出现dup指令,为此我再进行了如下验证:

int i = 0; 

int j = i++; 

Console.WriteLine( j );

得到的IL代码为:

.maxstack  3

.locals init (int32 V_0,

         int32 V_1)

IL_0000:  ldc.i4.0

IL_0001:  stloc.0

IL_0002:  ldloc.0

IL_0003:  dup

IL_0004:  ldc.i4.1

IL_0005:  add

IL_0006:  stloc.0

IL_0007:  stloc.1

IL_0008:  ldloc.1

IL_0009:  call       void [mscorlib]System.Console::WriteLine(int32)

IL_000e:  ret

dup指令再次出现了,同理我还验证了:

++i / i-- / --i

的情况

那么为什么会出现这样的情况呢?

Java中有一段类似此问题解释:

在这里jvm里面有两个存储区,一个是暂存区(是一个堆栈,以下称为堆栈),另一个是变量区。
语句istore_1是将堆栈中的值弹出存入相应的变量区(赋值);语句iload_1是将变量区中的值暂存如堆栈中。
因为i = i++;是先将i的值(0)存入堆栈,然后对变量区中的i自加1,这时i的值的确是1,但是随后的istore_1又将堆栈的值(0)弹出赋给变量区的i,所以最后i = 0。
又因为i = ++i;是先对变量区中的i自加1,然后再将变量区中i的值(1)存入堆栈,虽然最后执行了istore_1,但也只是将堆栈中的值(1)弹出赋给变量区的i,所以i = ++i;的结果是i = 1。

我想CLR的实现机制应该是与JVM中基本相同的,也理解了为什么结果不是1的原因了,可还有个疑问,不管Java还是C#为什么要这样设计呢?这样设计有什么好处吗?

然后我注意到了MSDN中的一句言简意赅话:

第一种形式是前缀增量操作。该操作的结果是操作数加 1 之后的值。

第二种形式是后缀增量操作。该运算的结果是操作数增加之前的值

回头再来看我们的代码:

int j = i++;

j是什么?j就是i++的结果,MSDN中说明了i++的结果就是i在这条语句执行之前的值,很明显j就是0了,那么对于:

i = i++;

呢?这里的意思很明显就是i最后的结果就是i++的结果,i++的结果是0,所以i最后也就是0了,为了实现++运算符的定义(该运算的结果是操作数增加之前的值),所以在IL中使用dup指令对i之前的值(i++的结果)进行了拷贝(暂存),我想这样理解问题就会简单很多了

以上只代表个人观点,欢迎拍砖:)

你可能感兴趣的:(C#)