读懂IL代码就这么简单 (一)
一前言
感谢 @冰麟轻武 指出文章的错误之处,现已更正
对于IL代码没了解之前总感觉很神奇,初一看完全不知所云,只听高手们说,了解IL代码你能更加清楚的知道你的代码是如何运行相互调用的,此言一出不明觉厉。
然后开始接触IL,了解了一段时后才发现原来读懂IL代码并不难。进入正题
1.1 什么是IL
IL是.NET框架中中间语言(Intermediate Language)的缩写。使用.NET框架提供的编译器可以直接将源程序编译为.exe或.dll文件,但此时编译出来的程序代码并不是CPU能直接执行的机器代码,而是一种中间语言IL(Intermediate Language)的代码(来源百度)
1.2 为什么要了解IL
在很多时候不明白代码是如何操作时就可以通过IL指令来解释,比如,装箱,拆箱是否只是听别人说或者书上讲是怎么怎么实现的,自己是否证实过呢?了解IL指令你可清楚看到是每一步是如何处理的
1.3 怎么学IL
世上有个定律叫“二八定律” ,80%的功能,只要用20%的技术就可以完成,但要完成另外20%可能就需要80%技术了,对于IL代码也是如此,有200多个指令,我们只需要用到其20%的指令就可以解决我们80%的问题了,所以我不会写太多,只是让大家能看懂普通的程序代码编译成IL代码后就行了,还有就是要多看,IL代码的每一条指令都是特定的意思,看得多了自然就懂了,当对自己代码有疑问时尝试看看它对应的IL代码,也许你会了解得更多。
IL指令大全 点这里
IL代码编译器 ILDasm 点这里
二 如何查看IL代码
2.1 步骤
1 编写代码并编译通过
2 找到源文件的obj文件下的 .exe文件
3 导入到ILDasm中反编译成IL代码
上图
1 -2步 3 导入到ILDasm中
ILDasm中图标含义
三 如何读IL(大致了解)
以上步骤完成后我们就可以看到代码被编译后的IL代码,以下部份将会对每一条IL指令做详细的解释
C#代码
static void Main(string[] args)
{
int i = ;
int j = ;
int k = ;
Console.WriteLine(i+j+k);
}
IL代码
// Call Stack是一个栈,而Call Stack中的Record Frame则是一个局部变量列表,用于存储 .locals init (int32 V_0,int32 V_1,int32 V_2)初始化后的参数 V_0,V_1,V_2
因图中没有把Record Frame 标记出来,所以自己画了一张图
// Evaluation Stack 是一个栈 ldc.i4.2 这种指令都会先把值压入栈中等待操作
在第四段时大家可以理解得更清楚一点
另外@Learning hard 指出IL指令中第 9 11 13行容易让人误解值是从Record Frame中加载的
现强调IL指令中 第 9 11 13行的ldc.i4.1,ldc.i4.2,ldc.i4.3 执行这几条指令时 值是还没有加载到Record Frame中的,但是MSDN也没有指出从哪里加载
所以只能根据个人的想法解释,程序在编译后值类型数据会存在线程栈中,所以我认为此时的9 11 13行的值是从线程栈中取的
.method private hidebysig static void Main(string[] args) cil managed
{
.entrypoint //程序入口
// Code size 19 (0x13)
.maxstack //定义函数代码所用堆栈的最大深度,也指Evaluation Stackk中最多能同时存在3个值
//以下我们把它看做是完成代码中的初始化
.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中的Record Frame中)
IL_0000: nop //即No Operation 没有任何操作,我们也不用管它
IL_0001: ldc.i4. //加载第一个变量"i"的值 (压入Evaluation Stack中)
IL_0002: stloc. //从栈中把"i"的值弹出并赋值给Record Frame中第0个位置(V_0)
IL_0003: ldc.i4. //加载第二个变量"j"的值 (压入Evaluation Stack中)
IL_0004: stloc. //从栈中把"j"的值弹出并赋值给Record Frame中第1个位置(V_1)
IL_0005: ldc.i4. //加载第三个变量"k"的值 (压入Evaluation Stack中)
IL_0006: stloc. //从栈中把 "k"的值弹出并赋值给Record Frame中第2个位置(V_2)
//上面代码初始化完成后要开始输出了,所以要把数据从Record Frame中取出 IL_0007: ldloc. //取Record Frame中位置为0的元素(V_0)的值("i"的值)并压入栈中 (相当于Copy一份值Call Stack中V_0的值。V_0本身的值是不变的)
IL_0008: ldloc. //取Record Frame中位置为1的元素(V_1)的值("j"的值)并压入栈中 (同上)
IL_0009: add // 做加法操作
IL_000a: ldloc. // 取出Record Frame中位置为2的元素(V_2)的值("k"的值)并压入栈中
IL_000b: add // 做加法操作
IL_000c: call void [mscorlib]System.Console::WriteLine(int32) //调用输出方法
IL_0011: nop
IL_0012: ret //即为 return 标记 返回值
} // end of method Program::Main
指令详解
.maxstack:评估堆栈(Evaluation Stack)可容纳数据项的最大个数
.locals init (int32 V_0,int32 V_1,int32 V_2):定义变量并存入Call Stack中的Record Frame中
nop:即No Operation 没有任何操作,我们也不用管它,
ldstr.:即Load String 把字符串加压入Evaluation Stack中
stloc.:把Evaluation Stack中的值弹出赋值到Call Stack中的Record Frame中
ldloc.:把Call Stack中的Record Frame中指定位置的值取出(copy)存入 Evaluation Stack中 以上两条指令为相互的操作stloc赋值,ldloc取值
call: 调用指定的方法
ret: 即return 标记返回
每一句IL代码都加了注释后,是不是觉得IL代码其实并不难呢,因为它的每一条指令都是固定的,你只要记住了,看IL代码就比较轻松了。
四 如何读IL(深入了解)
4.1 提出问题
有了上面的一点IL基础后,现在我们来深入一点点,
有如下几个问题:
1 当 ldc.i4.1 这一指定加载 “i” 这个变量后并没有马上赋值给Record Frame中的元素,而是要执行 stloc.0 后才赋值,那没赋值前是存在哪里的呢?
2 ldloc.0 把元素取出来后,存在哪里的?
3 add操作完成后值存在哪里?
4.2 概念引入
Managed Heap::這是動態配置(Dynamic Allocation)的記憶體,由 Garbage Collector(GC)在執行時自動管理,整個 Process 共用一個
Managed Heap(我理解为托管堆,存储引用类型的值)。
Evaluation 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指令详解
对三个名词做解释后现在我们再来仔细看看执行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.S IL_0001: ldc.i4.1 //加载第一个变量i
当执行这一条指令时会把变量i 的值压入Evaluation Stack中做临时存储
IL_0002: stloc.0 //把i 赋值给Call Stack中第0个位置
当执行这一条指信时会把Evaluation Stack 中的 i 弹出赋值给Record Frame中的第0个位置
IL_0007: ldloc.0 //取出Record Frame位置为0的元素 (i)
当执行这条指令时会将 Record Frame中的位置为0的元素的值取出(copy)压入Evaluation Stack 等待做加法的指令 Add
IL_000b: add // 做加法操作
add这一操作完成后,会把结果存在Evaluation Stack中等待下一步的指令操作
4.4 问题回答
以上内容看完开始的问题相应也解决了
1 ldc.i4.1 把值取出来后先存在 Evaluation Stack中 执行了stloc.0 后才会存入Record Frame中指定的元素中
2 ldloc.0 把取出来后也是先压入 Evaluation Stack 等持指令
3 add 操作完成后值是暂存于 Evaluation Stack中的 以上把IL指令是如何操作内存中的值做了一点很基本的介绍,让大家在了解IL指令时,知道是如何操作内存中的值的。我想对于理解IL指令或许更透彻一点。
五 总结
这一篇只写了IL中最基本的几个指令,然后讲解了IL指令是如何操作内存中数据的。古人云:水得一口一口喝,路得一步一步走,步子迈得大了容易扯着蛋,慢慢来内容虽然少了点,但是还会有下篇的。下一篇还是会写IL的一些基本指令,我会结合我自己的理解,尽量把文字写得通俗一点,让大家更容易理解。 另外本人水平有限,难免会有理解错误的地方,如有发现,请指出!我会马上修改,以免误导他人。 如果您觉得本文能给您带来一点收获不妨点下 推荐 让更多的人了解IL,您的推荐是我源源不断的写作力
如果觉得我的博客还不错,那就关个注吧~ 成长在于积累
参考资料:《你必须知道的.net》,MSDN
Record Frame
读懂IL代码就这么简单 (一)的更多相关文章
- 读懂IL代码就这么简单(三)完结篇
一 前言 写了两篇关于IL指令相关的文章,分别把值类型与引用类型在 堆与栈上的操作区别详细的写了一遍 这第三篇也是最后一篇,之所以到第三篇就结束了,是因为以我现在的层次,能理解到的都写完了,而且个人认 ...
- 读懂IL代码就这么简单(二)
一 前言 IL系列 第一篇写完后 得到高人指点,及时更正了文章中的错误,也使得我写这篇文章时更加谨慎,自己在了解相关知识点时,也更为细致.个人觉得既然做为文章写出来,就一定要保证比较高的质量,和正确率 ...
- 读懂IL代码就这么简单 ---- IL系列文章
读懂IL代码就这么简单 (一) 读懂IL代码就这么简单(二) 读懂IL代码就这么简单(三)完结篇 出处:http://www.cnblogs.com/zery/tag/IL%20%E7%B3%BB%E ...
- 【转载】读懂IL代码就这么简单(三)完结篇
一 前言 写了两篇关于IL指令相关的文章,分别把值类型与引用类型在 堆与栈上的操作区别详细的写了一遍这第三篇也是最后一篇,之所以到第三篇就结束了,是因为以我现在的层次,能理解到的都写完了,而且个人认为 ...
- 【转载】读懂IL代码就这么简单(二)
一 前言 IL系列 第一篇写完后 得到高人指点,及时更正了文章中的错误,也使得我写这篇文章时更加谨慎,自己在了解相关知识点时,也更为细致.个人觉得既然做为文章写出来,就一定要保证比较高的质量,和正确率 ...
- 读懂IL代码就这么简单
原文地址:http://www.cnblogs.com/zery/p/3366175.html 一前言 感谢 @冰麟轻武 指出文章的错误之处,现已更正 对于IL代码没了解之前总感觉很神奇,初一看完全不 ...
- 【转载】读懂IL代码就这么简单 (一)
一前言 感谢 @冰麟轻武 指出文章的错误之处,现已更正 对于IL代码没了解之前总感觉很神奇,初一看完全不知所云,只听高手们说,了解IL代码你能更加清楚的知道你的代码是如何运行相互调用的,此言一出不明觉 ...
- 读懂IL代码(一)
以前刚开始学C#的时候,总有高手跟我说,去了解一下IL代码吧,看懂了你能更加清楚的知道你写出来的代码是如何运行互相调用的,可是那时候没去看,后来补的,其实感觉也不晚.刚开始看IL代码的时候,感觉非常吃 ...
- 读懂IL代码(四)
这一篇是IL系列的最后一篇的,主要是要说一下IL中的流程控制.我相信,经过前面三篇的介绍,看IL代码应该不是什么大问题了吧.好吧,闲话不多说了,就来简单的说一下吧. 还是跟前几篇一样,以例子来解释说明 ...
随机推荐
- C++自定义修饰键,实现如<Capslock+J>等组合键的按键映射
前:所谓修饰键,就是Ctrl,Alt,Shift,Win这些按键. Update: 我使用AHK写了一个功能更丰富的脚本:https://github.com/h46incon/ModifierCus ...
- Hello BIEE
这篇文章提供了一个Hello World式的例子,讲述如何创建一个最简单的BIEE资料库.本文使用的示例数据可以在从此链接下载:http://www.zw1840.com . 目录 创建资料库 创 ...
- Spring Batch 批处理框架
<Spring Batch 批处理框架>基本信息作者: 刘相 出版社:电子工业出版社ISBN:9787121252419上架时间:2015-1-24出版日期:2015 年2月开本:16开页 ...
- ORA-00600: internal error code, arguments: [kcblasm_1], [103], [], [], [], [], [], []
一ORACLE 10.2.0.5.0 标准版的数据库的告警日志出现ORA-00600错误,具体错误信息如下所示 Errors in file /u01/app/oracle/admin/SCM2/bd ...
- Sql Server之旅——第四站 你必须知道的非聚集索引扫描
非聚集索引,这个是大家都非常熟悉的一个东西,有时候我们由于业务原因,sql写的非常复杂,需要join很多张表,然后就泪流满面了...这时候就 有DBA或者资深的开发给你看这个猥琐的sql,通过执行计划 ...
- 树莓派mjpg-stream摄像头监控
Q:463431476 第一步: 1.安装依赖 sudo apt-get install libv4l-dev libjpeg8-dev imagemagick 第二步: 下载SVN sudo ...
- Python 变量类型
Python 变量类型 变量存储在内存中的值.这就意味着在创建变量时会在内存中开辟一个空间. 基于变量的数据类型,解释器会分配指定内存,并决定什么数据可以被存储在内存中. 因此,变量可以指定不同的数据 ...
- 单源最短路径算法——Dijkstra算法
#include <stdio.h> #include <stdlib.h> #include <string.h> /* run this program usi ...
- 在win10中创建开机自动登陆的网络驱动器
前提环境: win10系统电脑一台. centos系统电脑一台,在该系统中安装samba,并配置共享. 要解决的问题: 在win10做了网络映射,将centos共享的文件夹设置成本地硬盘.在映射时,也 ...
- DedeCms文档关键词替换,优先替换长尾关键词
本文教大家:dedecms文档关键词维护之关键词出现多次,只给出现的第一个加链接的 举例:当文章中出现了一百次台历时,按官方的原理,他会给一百个台历都加上链接的.dedecms这如何是好? 解决方法( ...