您的位置:首页 > 其它

从MSIL角度分析.net中++i和i++具体的区别

2012-03-17 23:21 267 查看
在学.net的第一天估计就已经学到了++i和i++的区别 表现上讲 i++完成后 i并没有被马上赋值为i+1 或者说需要被赋值为i++的值不会得到这个更新后的i值 而++i的话i的值会马上会赋值为i+1 同样需要被赋值为i+1的变量会马上得到更新后的i+1值

从百度知道上是这么解释的

i++ 先执行此行语句再i=i+1
++i 先执行i=i+1再执行此行语句


虽然容易理解 但是编程中经常性的也会出现i++不行的话就试试++i的情况

比如下面这行代码

using System;
namespace VimTest
{
public class VimeTest
{
public static void Main(string[] arg)
{
int i=0;
int j=0;
j=i++;
Console.WriteLine(i);
Console.WriteLine(j);
Console.ReadLine();
}
}
}


最后的输出如果不仔细想的话会认为是i=j=1 但是事实上i=1而j=0

如果换一种方式 j=++i 则最后的输出会是i=j=1

好吧其实死记硬背还是记得住的 但是总会想知道背后到底是怎么回事 正好这几天在学习MSIL 所以打算把这行程序反汇编一下看看到底是为什么导致了++在前和在后的区别

首先反汇编j==i++的情况

.method public hidebysig static void Main(string[] arg) cil managed

{

.entrypoint

// 代码大小 32 (0x20)

.maxstack 3

.locals init (int32 V_0,

int32 V_1)

IL_0000: nop

IL_0001: ldc.i4.0

IL_0002: stloc.0 i 赋值为0 索引0

IL_0003: ldc.i4.0

IL_0004: stloc.1 j 赋值为0 所以1

IL_0005: ldloc.0 索引0元素i入栈

IL_0006: dup 复制栈顶元素的值 复制后的值入栈

IL_0007: ldc.i4.1 数值1入栈

IL_0008: add 栈顶两个元素相加 结果入栈

IL_0009: stloc.0 出栈 将栈顶元素赋值给索引为0的变量 即i 此时i值为栈顶元素的值1

IL_000a: stloc.1 同上 但是此时因为栈顶元素值为0 所以j值为0

IL_000b: ldloc.0

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

IL_0011: nop

IL_0012: ldloc.1

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

IL_0018: nop

IL_0019: call string [mscorlib]System.Console::ReadLine()

IL_001e: pop

IL_001f: ret

} // end of method VimeTest::Main

忽略掉最后几行函数调用 我们来看下i++到底做了什么

首先0到4行是初始化,i和j分别索引为0,1 第五行,意思是将本地变量0(i)入栈 第六行是重点,dup指令复制栈顶元素 并入栈 实质就是生成了一个临时变量temp,temp=i

第7行 将数值1入栈 第8行 将栈顶两个元素相加 并将结果入栈 最后结果就是 栈顶元素值为1 栈顶之后的第二个元素(就是刚刚复制的那个i)0,注意这是很重要的一点,因为最后的赋值涉及到出栈顺序 第9行 栈顶元素出栈 赋值给索引为0的变量 也就是i,这个时候i=1,而我们的栈顶元素现在则是原始的i值0,第10行,栈顶元素出栈,赋值给索引为1的变量,也就是j,所以最后 j=0

再来看j=++i的情况

反汇编的MSIL代码

.method public hidebysig static void Main(string[] arg) cil managed

{

.entrypoint

// 代码大小 32 (0x20)

.maxstack 2

.locals init (int32 V_0,

int32 V_1)

IL_0000: nop

IL_0001: ldc.i4.0

IL_0002: stloc.0

IL_0003: ldc.i4.0

IL_0004: stloc.1

IL_0005: ldloc.0

IL_0006: ldc.i4.1

IL_0007: add

IL_0008: dup

IL_0009: stloc.0

IL_000a: stloc.1

IL_000b: ldloc.0

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

IL_0011: nop

IL_0012: ldloc.1

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

IL_0018: nop

IL_0019: call string [mscorlib]System.Console::ReadLine()

IL_001e: pop

IL_001f: ret

} // end of method VimeTest::Main

看上去和i++的情况差不多 唯一不同的是dup指令所在的位置 dup指令在第8行而add在第7行 也就意味着栈顶的复制是发生在相加之后的 这样 最后出栈的时候两个值均为1 也就是i=j=1

通过上面的分析 大致就理清楚了i++和++i真正的区别在什么地方 对于.net来说 执行自增操作的时候 会将需要自增的值复制一份入栈 而++在前还是在后则决定了这个复制是发生在自增之前还是之后 如果是i++ 则会导致复制出来的值+1而原始值没有+1 最后出栈的顺序导致了一个为自增的值而另一个为没有自增的值 反之 对于++i 由于复制操作发生在相加操作之后 所以栈顶两个元素都为自增后的值 也就使得i和j都为1
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: