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

昨天看见部门面试新员工的一道面试题,题目如下:

int i =1;

i=i++;

console.write(i);

 

/

我想当然的认为,这只是一道简单的自增问题,即先赋值再自增,最后j的值为2,然而正确答案却出乎我的意料,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

在查阅了一些相关资料之后,对这个问题有了一定的认识。与其说这是C#(包括Java)与C++语言的区别,不如说这是两种编译器之间的差别,为了替编译器在优化代码的过程中去掉一些不必要的限制,C,C++等编译器都不约而同的保留了一些Undefine的语法元素,它们的执行逻辑随编译器的不同而不同。而本文的问题就是一个例子,C#编译器按照以下的逻辑分析表达式:

Instructions    Stack      Variable

                           1
ldloc i         1          1
dup             1, 1       1
ldc.i4.1        1, 1, 1    1
add             1, 2       1
stloc i         1          2
stloc i                    1

而C++的处理逻辑是这样的:

Instructions    Stack   Variable

                        1
ldloc i         1       1
stloc i                 1
ldloc i         1       1
ldc.i4.1        1, 1    1
add             2       1
stloc i                 2

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

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

可以看出就因为这个dup指令才导制了最后i的值还是变成了1,值得注意的是,只有赋值运算,才会去执行dup指令,如果仅仅是纯粹的自增,则C#编译器和C++编译器的处理过程却是一样的。

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

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

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

我想CLR的实现机制应该是与JVM中基本相同的,也理解了为什么结果不是2的原因了,对于这个问题,我觉得简单的从运算符的定义去了解,更容易记忆。注意到MSDN中的一句言简意赅话:

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

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

i=i++与j=i++的本质是一样的,都是用赋值运算符左边的变量去保存赋值运算符右边变量运算之前的值,我想C#编译器之所以用dup命令备份变量的值,正是出于这个目的。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值