zoukankan      html  css  js  c++  java
  • 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#泛型类的简单创建与使用
    线程、委托、lambda运算符的简单示例
    异步编程中使用帮助类来实现Thread.Start()的示例
    C#操作INI配置文件示例
    C#“简单加密文本器”的实现
    Java设计模式之模板模式(Template )
    java提取出一个字符串里面的Double类型数字
    阿里云服务器配置SSL证书成功开启Https(记录趟过的各种坑)
    Gson解决字段为空是报错的问题
    shiro 单点登录原理 实例
  • 原文地址:https://www.cnblogs.com/XiaoG/p/1549826.html
Copyright © 2011-2022 走看看