收藏 分享(赏)

读懂IL代码就这么简单.doc

上传人:tkhy51908 文档编号:8159046 上传时间:2019-06-11 格式:DOC 页数:14 大小:453.50KB
下载 相关 举报
读懂IL代码就这么简单.doc_第1页
第1页 / 共14页
读懂IL代码就这么简单.doc_第2页
第2页 / 共14页
读懂IL代码就这么简单.doc_第3页
第3页 / 共14页
读懂IL代码就这么简单.doc_第4页
第4页 / 共14页
读懂IL代码就这么简单.doc_第5页
第5页 / 共14页
点击查看更多>>
资源描述

1、一前言感谢 冰麟轻武 指出文章的错误之处,现已更正对于 IL 代码没了解之前总感觉很神奇,初一看完全不知所云,只听高手们说,了解IL 代码你能更加清楚的知道你的代码是如何运行相互调用的,此言一出不明觉厉。然后开始接触 IL,了解了一段时后才发现原来读懂 IL 代码并不难。进入正题1.1 什么是 ILIL 是.NET 框架中中间语言(Intermediate Language)的缩写。使用.NET 框架提供的编译器可以直接将源程序编译为.exe 或.dll 文件,但此时编译出来的程序代码并不是 CPU 能直接执行的机器代码,而是一种中间语言 IL(Intermediate Language)的代

2、码(来源百度)1.2 为什么要了解 IL在很多时候不明白代码是如何操作时就可以通过 IL 指令来解释,比如,装箱,拆箱是否只是听别人说或者书上讲是怎么怎么实现的,自己是否证实过呢?了解 IL 指令你可清楚看到是每一步是如何处理的1.3 怎么学 IL世上有个定律叫“ 二八定律 ” ,80%的功能,只要用 20%的技术就可以完成,但要完成另外 20%可能就需要 80%技术了,对于 IL 代码也是如此,有 200 多个指令,我们只需要用到其 20%的指令就可以解决我们 80%的问题了,所以我不会写太多,只是让大家能看懂普通的程序代码编译成 IL 代码后就行了,还有就是要多看,IL 代码的每一条指令都

3、是特定的意思,看得多了自然就懂了,当对自己代码有疑问时尝试看看它对应的 IL 代码,也许你会了解得更多。二 如何查看 IL 代码2.1 步骤1 编写代码并编译通过2 找到源文件的 obj 文件下的 .exe 文件3 导入到 ILDasm 中反编译成 IL 代码上图1 -2 步 3 导入到 ILDasm 中 ILDasm 中图标含义三 如何读 IL(大致了解)以上步骤完成后我们就可以看到代码被编译后的 IL 代码,以下部份将会对每一条 IL指令做详细的解释 http:/ C#代码 1 static void Main(string args)2 3 int i = 1;4 int j = 2;5

4、 int k = 3;6 Console.WriteLine(i+j+k);7 IL 代码/ Call Stack 是一个局部变量列表,用于存储 .locals init (int32 V_0,int32 V_1,int32 V_2)初始化后的参数 V_0,V_1,V_2/ Evaluation Stack 是一个栈 ldc.i4.2 这种指令都会先把值压入栈中等待操作在第四段时大家可以理解得更清楚一点1 .method private hidebysig static void Main(string args) cil managed2 3 .entrypoint /程序入口4 / Cod

5、e size 19 (0x13)5 .maxstack 3 /定义函数代码所用堆栈的最大深度,也可理解为 Call Stack 的变量个数6 /以下我们把它看做是完成代码中的初始化7 .locals init (int32 V_0,int32 V_1,int32 V_2) /定义 int 类型参数 V_0,V_1,V_2 (此时已经把 V_0,V_1,V_2 存入了 Call Stack 中)8 IL_0000: nop /即 No Operation 没有任何操作,我们也不用管它9 IL_0001: ldc.i4.1 /加载第一个变量“i“ (压入 Evaluation Stack 中) 1

6、0 IL_0002: stloc.0 /把“i“赋值给 Call Stack 中第 0 个位置(V_0) 11 IL_0003: ldc.i4.2 /加载第二个变量“j“ (压入 Evaluation Stack 中) 12 IL_0004: stloc.1 /把“j“赋值给 Call Stack 中第 1 个位置(V_1)13 IL_0005: ldc.i4.3 /加载第三个变量“k“ (压入Evaluation Stack 中)14 IL_0006: stloc.2 /把 “k“ 赋值给 Call Stack 中第 2 个位置(V_2)15 http:/ 16 /上面代码初始化完成后要开始

7、输出了,所要把数据从 Call Stack 中取出17 18 IL_0007: ldloc.0 /取 Call Stack 中位置为 0 的元素(V_0)的值(“i“的值) (相当于 Copy 一份值 Call Stack 中 V_0 的值。V_0 本身的值是不变的)19 IL_0008: ldloc.1 /取 Call Stack 中位置为 1 的元素(V_1)的值(“j“的值) (同上)20 IL_0009: add / 做加法操作21 IL_000a: ldloc.2 / 取出 Call Stack 中位置为 2 的元素(V_2)的值(“k“的值)22 IL_000b: add / 做加

8、法操作23 IL_000c: call void mscorlibSystem.Console:WriteLine(int32) /调用输出方法24 IL_0011: nop25 IL_0012: ret /即为 return 标记 返回值26 / end of method Program:Main指令详解.maxstack:评估堆栈(Evaluation Stack)可容纳数据项的最大个数.locals init (int32 V_0,int32 V_1,int32 V_2):定义变量并存入 Call Stack 中nop:即 No Operation 没有任何操作,我们也不用管它,lds

9、tr.:即 Load String 把字符串加压入 Evaluation Stack 中 stloc.:把 Evaluation Stack 中的值弹出赋值到 Call Stack 中ldloc.:把 Call Stack 中指定位置的值取出(copy) 存入 Evaluation Stack 中 以上两条指令为相互的操作 stloc 赋值, ldloc 取值call: 调用指定的方法ret: 即 return 标记返回每一句 IL 代码都加了注释后,是不是觉得 IL 代码其实并不难呢,因为它的每一条指令都是固定的,你只要记住了,看 IL 代码就比较轻松了。四 如何读 IL(深入了解 )4.1

10、 提出问题有了上面的一点 IL 基础后,现在我们来深入一点点,有如下几个问题:1 当 ldc.i4.1 这一指定加载 “i” 这个变量后并没有马上赋值给 Call Stack 中的元素,而是要执行 stloc.0 后才赋值,那没赋值前是存在哪里的呢?2 ldloc.0 把元素取出来后,存在哪里的?3 add 操作完成后值存在哪里?4.2 概念引入Managed Heap::這是動態配置(Dynamic Allocation)的記憶體,由 Garbage Collector(GC)在執行時自動管理,整個 Process 共用一個 Managed Heap(我理解为托管堆,存储引用类型的值)。Ev

11、aluation Stack:這是由 .NET CLR 在執行時自動管理的記憶體,每個 Thread 都有自己專屬的 Evaluation Stack(我理解为类似一个临时存放值类型数据的线程栈)Call Stack:這是由 .NET CLR 在執行時自動管理的記憶體,每個 Thread 都有自己專屬的 Call Stack。每呼叫一次 method,就會使得 Call Stack 上多了一個 Record Frame;呼叫完畢之後,此 Record Frame 會被丟棄(我理解为一个局部变量表,用于存放.locals init(int32 V_0)指令的参数值如:V_0)4.3 IL 指令详

12、解对三个名词做解释后现在我们再来仔细看看执行 IL 指令时,对应的变量是如何存放的IL_0001: ldc.i4.1 /加载第一个变量 i 首先对 ldc.i4.1 做下细解:变量的值为 1 时 IL 指令就是 ldc.i4.1 ,变量值为 2 时 IL指令就是 ldc.i4.2,依此类推一直到 ldc.i4.8 当为-1 时 IL 指令为 ldc.i4.M1,当超过 8 时就是一个统一指令 ldc.i4.SIL_0001: ldc.i4.1 /加载第一个变量 i当执行这一条指令时会把变量 i 的值压入 Evaluation Stack 中做临时存储IL_0002: stloc.0 /把 i

13、赋值给 Call Stack 中第 0 个位置当执行这一条指信时会把 Evaluation Stack 中的 i 弹出赋值给 Call Stack 中的第 0 个位置 IL_0007: ldloc.0 /取出 Call Stack 位置为 0 的元素 (i)当执行这条指令时会将 Call Stack 中的位置为 0 的元素的值取出(copy)压入 Evaluation Stack 等待做加法的指令 AddIL_000b: add / 做加法操作add 这一操作完成后,会把结果存在 Evaluation Stack 中等待下一步的指令操作4.4 问题回答以上内容看完开始的问题相应也解决了1 ld

14、c.i4.1 把值取出来后先存在 Evaluation Stack 中 执行了 stloc.0 后才会存入Call Stack 中指定的元素中2 ldloc.0 把取出来后也是先压入 Evaluation Stack 等持指令3 add 操作完成后值是暂存于 Evaluation Stack 中的以上把 IL 指令是如何操作内存中的值做了一点很基本的介绍,让大家在了解 IL 指令时,知道是如何操作内存中的值的。我想对于理解 IL 指令或许更透彻一点。五 总结这一篇只写了 IL 中最基本的几个指令,然后讲解了 IL 指令是如何操作内存中数据的。古人云:水得一口一口喝,路得一步一步走,步子迈得大了

15、容易扯着蛋,慢慢来内容虽然少了点,但是还会有下篇的。下一篇还是会写 IL 的一些基本指令,我会结合我自己的理解,尽量把文字写得通俗一点,让大家更容易理解。另外本人水平有限,难免会有理解错误的地方,如有发现,请指出!我会马上修改,以免误导他人。如果您觉得本文能给您带来一点收获不妨点下 推荐 让更多的人了解 IL,您的推荐是我源源不断的写作力如果觉得我的博客还不错,那就关个注吧成长在于积累IL 系列 第一篇写完后 得到高人指点,及时更正了文章中的错误,也使得我写这篇文章时更加谨慎,自己在了解相关知识点时,也更为细致。个人觉得既然做为文章写出来,就一定要保证比较高的质量,和正确率 。知识点回顾:Ma

16、naged Heap(托管堆): 用于存放引用类型的值Evaluation Statck(评估栈):临时存放值类型数据,引用类型地址的堆栈(这个是栈,所以遵循栈的操作特点,先进后出)Call Stack(局部变量列表):,用于存放.locals init(int32 V_0)指令的参数值如:V_0 (不要被 Call Stack 这个名字所误导了, 它并不是一个栈 )二 指令详解(基本介绍)2.1 知识点介绍在第一篇时,我只详细的写了值类型的 IL 指令,这一篇会主要以引用类型为主,这一篇会有装箱操作,所以先写一下装箱操作在内存中是如何操作的装箱操作:1 内存分配,在托管堆中分配内存空间,2

17、将值类型的字段拷贝到新分配的内存中,3 将托管堆中的对象地址返回给新的对象操作过程如下图C#代码 1 /*2 Author:zery-zhang3 BlogAddress:http:/ */5 static void Main(string args)6 7 string name = “Zery“;8 int age = 22;9 Console.WriteLine(age.ToString() + name);/已 ToString 的操作10 Console.WriteLine(age+name);/未 ToString 操作11 12 IL 代码 1 /*2 Author:zery-z

18、hang3 BlogAddress:http:/ */5 .method private hidebysig static void Main(string args) cil managed6 7 .entrypoint8 / Code size 48 (0x30)9 10 /以下代码 完成 C#代码中初始化变量的操作11 12 /评估栈(Evaluation Stack) 可容纳数据项的最大个数13 .maxstack 2 14 /定义并初始化参数 并存入 局部变量表(Call Stack)中15 .locals init (string V_0,int32 V_1) 16 IL_0000

19、: nop17 /把字符串压入评估栈(Evaluation Stack)中18 IL_0001: ldstr “Zery“ 19 / 从评估栈中弹出(“Zery“) 字符,并赋值给局部变量表中第 0 个位置的元素 V_020 IL_0006: stloc.0 21 /把整数 22 压入评估栈中 22 IL_0007: ldc.i4.s 22 23 /把整数 22 弹出,并赋值给局部变量表中第 1 个位置的元素 V_124 IL_0009: stloc.1 25 26 /以下代码完成 C#中的输出操作27 28 /取出局部变量表中 V_1 元素的值 “22“ (copy)并压入评估栈中29 IL

20、_000a: ldloca.s V_1 30 /弹出刚刚压入的值(“22“)调用 ToString 方法转成 string 类型并将引用存入评估栈中31 IL_000c: call instance string mscorlibSystem.Int32:ToString() 32 /取出局部变量表中第 0 个位置元素(V_0)的值(“Zery“)压入评估栈中(此时评估栈中有两个值,指向推管堆中“22“的引用地址和字符串“Zery“)33 IL_0011: ldloc.0 34 /弹出评估栈中两个值调用 String 的 Concat 方法把字个字符拼接存入托管堆中(Managed Heap

21、)并返回地址压入评估栈中35 IL_0012: call string mscorlibSystem.String:Concat(string,string) 36 /调用输出方法,调用输出方法后评估栈中的值(指向托管堆字符的地址)会被回收 。37 IL_0017: call void mscorlibSystem.Console:WriteLine(string) 38 39 /未 ToString 的操作40 IL_001c: nop41 /取局部变量表中第 1 个位置的元素 V_1 的值 (“22“) 压入评估栈中42 IL_001d: ldloc.1 43 /把刚刚压入的整数 22 装

22、箱并返回指向托管堆的地址存入评估栈中44 IL_001e: box mscorlibSystem.Int32 45 /取局部变量表中第 0 个位置的 f 元素 V_0 的值(“Zery“)并压入评估栈中46 IL_0023: ldloc.0 47 /弹出评估栈中两个值调用 String 的 Concat 方法把字个字符拼接存入托管堆中(Managed Heap )并返回地址压入评估栈中48 IL_0024: call string mscorlibSystem.String:Concat(object,object) 49 /调用输出方法50 IL_0029: call void mscorl

23、ibSystem.Console:WriteLine(string) 51 IL_002e: nop52 /标记返回53 IL_002f: ret 54 / end of method Program:Main2.2 IL 指令详解.maxstack:评估堆栈(Evaluation Stack)可容纳数据项的最大个数.locals init (int32 V_0,int32 V_1,int32 V_2):定义变量并存入 Call Stack 中nop:即 No Operation 没有任何操作,我们也不用管它,ldstr.:即 Load String 把字符串加压入 Evaluation St

24、ack 中 stloc.:把 Evaluation Stack 中的值弹出赋值到 Call Stack 中ldloc.:把 Call Stack 中指定位置的值取出(copy) 存入 Evaluation Stack 中 以上两条指令为相互的操作 stloc 赋值,ldloc 取值call: 调用指定的方法box:执行装箱操作ret: 即 return 标记返回三 指令详解(深入介绍)如果看代码中的注释你还不是很理解,那就看看下面的图解过程吧,如果每一步都画图,那工程太大了,所以我会把简单的的步组合成一张图并做上注释先画初始化的代码详解图 注: 为了减少图片所以栈的弹出与压入操作就省去了,都只

25、画出了结果IL_0001: ldstr “Zery“IL_0006: stloc.0IL_0007: ldc.i4.s 22IL_0009: stloc.1因为字符串是引用类型,所以是保存在托管堆中,而栈中只保存对字符引用的地址,可以看到图中的字符串是在托管中的,而评估栈中只保存了引用IL_000a: ldloca.s V_1IL_000c: call instance string mscorlibSystem.Int32:ToString()IL_0011: ldloc.0IL_0012: call string mscorlibSystem.String:Concat(string,st

26、ring)IL_0017: call void mscorlibSystem.Console:WriteLine(string)IL_001d: ldloc.1IL_001e: box mscorlibSystem.Int32IL_0023: ldloc.0IL_0024: call string mscorlibSystem.String:Concat(object,object)IL_0029: call void mscorlibSystem.Console:WriteLine(string)装箱的过程图在上面已给出此处只把结果画出四 总结1 用两篇把 值类型与引用类型在内存中不同的位置

27、与不同的操作详细的写了,我觉得还是很有必要的,因为所有的数据类型由这两种类型组成,把这两种类型的操作了解了看其它 IL 指令就是透过本质看现象了。2 关于画图,我觉得画图是程序员必学的知识,牛 X 的程序员画出来的,系统架构图,系统设计图等都是很有结构很清晰的。我的画图技能还有太多不足,只是画这种简单的图都觉得,无法完美的表达自己头脑所想的那样, 如果觉得文章与给您带来一点 收获 那就 帮忙点个推荐 吧,也让更多的人能关注并了解 IL 您的推荐是我源源不断的写作力如果您希望您的技术路上能有更多的朋友,那就关注一下吧注:本人不才,水平有限,如有不对之处,希望能及时提出,我会马上更正,以免误导他人 谢谢!

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 企业管理 > 管理学资料

本站链接:文库   一言   我酷   合作


客服QQ:2549714901微博号:道客多多官方知乎号:道客多多

经营许可证编号: 粤ICP备2021046453号世界地图

道客多多©版权所有2020-2025营业执照举报