轻松读懂IL
来源:互联网 发布:js trigger事件 编辑:程序博客网 时间:2024/05/17 06:44
先说说学IL有什么用,有人可能觉得这玩意平常写代码又用不上,学了有个卵用。到底有没有卵用呢?暂且不说什么学了可以看看一些语法糖的实现,或对.Net理解更深一点这些虚头巴脑的东西。最重要的理由就是当面试官看你简历上写着精通C#时,问你一句:
“懂不懂IL?”
怎么回答?
“不好意思,那东西没什么卵用,所有没学。”
还是
“还行,可以探讨一下。”
你觉得哪个回答好呢,答得好才更有底气要到更多的薪资,多个几千也说不定,而这学起来花不了太多的时间。
很多人见到IL一大堆指令,和汇编一样,就感觉头大不想学了。其实IL本身逻辑很清楚,主要是把指令的意思搞明白就好办了。记指令只要记住规律就好,我把它们分为三类。
第一类:直观型
这一类的特点是一看名字就知道是干嘛的,不需要多讲,如下:
强调一下,三种Call用的场景不太一样:
- Call:常用于调用编译时就确定的方法,可以直接去元数据里找方法,如静态函数、实例方法,也可以Call虚方法,不过只是call这个类型本身的虚方法,和实例的方法性质一样。另外,Call不做null检测。
- Calli:MSDN上讲是直接调用指针指向的函数,具体场景没见过,有知道的朋友望不吝赐教。
- Callvirt:可以调用实例方法和虚方法,调用虚方法时以多态方式调用,不能调用静态方法。Callvirt调用时会做null检测,如果实例是null,会抛出NullReferenceException异常,所以速度比Call慢点。
第二类:加载(Ld)和存储(St)
我们知道,C#程序运行时会有线程栈把参数、局部变量放上来,另外有个计算栈用来做函数里的计算,所以需要指令把值加载到计算栈上,算完后再把计算栈上的值存到线程栈上去,Ld和St这类指令专门干这些活。
比方说Ldloc_0:
这个可以拆开来看,Ld可以理解为Load,也就是加载;loc可以理解为local variable,也就是局部变量;后面的_0表示索引。连起来的意思就是:把索引为0的局部变量加载到计算栈上。对应的Ldloc_1就是把索引为1的局部变量加载到计算栈上,以此类推。
知道了Ld的意思,下面这些指令也就很容易理解了:
Ldstr=load string,
Ldnull=load null,
Ldobj=load object,
Ldfld=load field,
Ldflda=load field address,
Ldsfld=load static field,
Ldsflda=load static field address,
Ldelem=load element in array,
Ldarg=load argument,
Ldc表示加载数值,如Ldc_I4_0表示加载Int32类型的数字0,Ldc_I4_1表示加载Int32类型的数字1,以此类推。
关于后缀
_i[n]:[n]表示字节数,1个字节是8位,所以8*n的int,比如i1、i2、i4、i8,i1就是int8(byte)、i2就是int16(short)、i4就是int32(int)、i8就是int64(long)。
相似的还有_u1、_u2、_u4、_u8,分别表示unsigned int8(byte)、unsigned int16(short)、unsigned int32(int)、unsigned int64(long)。
_R4、_R8表示float和double。
_ovf(overflow)则表示进行溢出检查,溢出时会抛出异常。
_un(unsigned)表示无符号数。
_ref(reference)表示引用。
_S(short)表示短格式,比如Ldc_I4_S表示将提供的int8值作为int32推送到计算堆栈上。
_[n]比如_1、_2等,如果跟在i[n]后面则表示数值,其他都表示索引。如Ldc_I4_1表示加载数值1到计算堆栈上,再如Ldarg_0就是加载第一个参数到计算堆栈上。
注意:
Ldarg要特别注意一个问题,如果是实例方法的话Ldarg_0加载的是本身即this,Ldarg_1加载的才是函数的第一个参数;如果是静态方法,Ldarg_0就是加载第一个参数。
与Ld对应的就是St,可以理解为Store,意思是把计算堆栈上的值存到变量中去,Ld相关的指令很多都有St对应的,比如Stloc、Stfld、Stsfld、Stobj、Starg、Stelem等,就不多说了。
第三类:比较指令,比较大小或判断bool值
有一部分指令是比较之后跳转的,代码里的if就会产生这些指令,符合条件则跳转执行另一些代码。
1、以b开头:beq、bge、bgt、ble、blt、bne
先把b去掉看看:
eq:equivalent with,==
ge:greater than or equivalent with,>=
gt:greater than,>
le:less than or equivalent with,<=
lt:less than,<
ne:not equivalent with,!=
这样就很好理解了,beq IL_0005就是计算栈上两个值相等的话就跳转到IL_005,ble IL_0023就是第一个值小于或等于第二个值就跳转到IL_0023。
2、以br(break)开头:br、brfalse、brtrue
br是无条件跳转;
brfalse表示计算栈上的值为false/null/0时发生跳转;
brtrue表示计算栈上的值为tue/非空/非0时发生跳转。
3、以c开头:ceq、cgt、clt
该指令的用来计算bool值的,跟前面b开头的有点像。
ceq比较两个值,相等则将1(true)推到栈上,否则就把0(false)推到栈上;
cgt比较两个值,第一个大于第二个则将1(true)推到栈上,否则就把0(false)推到栈上;
clt比较两个值,第一个小于第二个则将1(true)推到栈上,否则就把0(false)推到栈上。
总结
以上三类是IL常用指令,把这些搞明白了,IL指令也就理解的七七八八了,如果遇到不熟悉的指令查一下也没问题。
IL其实不难,有没有用则仁者见仁,智者见智,学起来花不了太多时间,也没必要学太深,不过要有耐心,复杂的IL看起来还真是挺头痛。好在有工具ILSpy,可以在option里选择部分不编译看起来会简单些。
最后介绍两个工具:
.Net Reflector可以把用户自己编写的IL指令转化为正常代码,大家可以自己下载安装;
IL查看工具可以把正常代码转化为IL指令,vs2010中路径为C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\Bin\NETFX 4.0 Tools\ildasm.exe,不同版本目录可能不太一样。
有了这两个工具当我们想用IL指令实现某一功能但不会写时,可以先用正常代码把功能写出来,在IL查看工具中查看IL代码是什么样的,然后自己再根据转化的IL代码逻辑使用IL指令实现想要的功能。
.Net Reflector Version 9.0.1.374 带注册机免费下载地址: http://download.csdn.net/detail/xiaouncle/9649783
最后我要感谢布鲁克石的文章:http://www.cnblogs.com/brookshi/p/5225801.html?ref=myread
- 轻松读懂IL
- 30分钟?不需要,轻松读懂IL
- 30分钟?不需要,轻松读懂IL
- 读懂IL代码
- IL轻松学习
- 一步步教你读懂NET中IL
- 读懂IL代码就这么简单 (一)
- 读懂IL代码就这么简单
- 读懂IL代码就这么简单 (一)
- 轻松读懂财报
- 一步步教你读懂NET中IL(附带图)
- 一步步教你读懂NET中IL(图文详解)
- IL
- 显卡帝教你读懂GPU架构图 轻松做达人
- 读懂
- 网管基础——轻松读懂路由表天书的条目
- 网管基础——轻松读懂路由表天书的条目
- 关于IL
- 记事本的基本功能
- Java:常用标准库算法
- C++ Primer(第五版)练习3.35
- 2016国庆清北Day2T1
- 51Nod-1020-逆序排序
- 轻松读懂IL
- 千兆网(2):数据的发送与接收测试
- poj 1942 Paths on a Grid (组合&阶乘处理)
- JTree (一)
- Android签名机制
- java访问hdfs简单demo
- oracle数据类型及函数
- 树的简单介绍
- 程序计数器简要介绍