从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=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
从百度知道上是这么解释的
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
相关文章推荐
- 关于 equals 和 == 的具体区别 从源码角度分析
- 从Java源码的角度来分析HashMap与HashTable的区别
- 我对美国与日本恐怖片的区别分析(国家性格角度)
- 从编译器角度分析C语言中数组名和指针的区别
- PHP、JAVA、.NET这三种技术的区别分析
- .NET中的静态与非静态的区别分析
- View的两种更新方法-从源码角度分析invalidate()和postInvalidate()的区别
- 从编译器角度分析C语言中数组名和指针的区别
- 基于.NET 2.0的GIS开源项目SharpMap分析手记(八):怎样对SharpMap进行扩展开发——从许可角度来谈
- 从编译器角度分析C语言中数组名和指针的区别
- 从编译器角度分析C语言中数组名和指针的区别
- 从对子类影响角度分析抽象类和接口的区别
- 从编译器角度分析C语言中数组名和指针的区别
- 从编译器角度分析C语言中数组名和指针的区别
- 从源码的角度分析Android中setClickable()和setEnable()的区别
- 从源码的角度分析Android中setClickable()和setEnable()的区别
- 从存储和分析角度看大数据和云计算区别与联系
- 从“分析”的角度谈OLAP、数据挖掘、统计分析三者之间的区别和联系
- 从汇编角度分析char*和char[]的区别
- Hashmap和hashtable三大区别(从源码角度分析为什么map可以存放一个key为null,多个值为null)的特点