-

一前言
感谢
@
冰麟轻武
指出文章的错误之处,现已更正
对于
IL
代
码没了解之前总感觉很神奇,初一看完全不知所云,只听高手们说,了解
IL
代码你能更加清楚的知道你的代码是如何运行相互调用的,此言一出不明觉厉。
然后开始接触
IL
,了解了一段
时后才发现原来读懂
IL
代码并不难。进入正题
1.1
什么是
IL
IL
是<
/p>
.NET
框架中中间语言
(
Intermediate Language
)
的
缩写。使用
.NET
框架
提供的
编译器
可以直接将源程序编译为
.exe
或
.dll
文件,但此时编译出来的程序代
码并不
是
CPU
能直接执行的机器代码
,而是一种中间语言
IL
(
Inter
mediate Language
)的代
码
< br>(
来源百度
)
1.2
为什么要了解
IL
在很多时候不明白代码是如何操作时就可以通过
IL
指令来解释,比如,装箱,拆箱
是否只是听别人说或者书上讲是怎么怎么实现的
,自己是否证实过呢?了解
IL
指令你可清
楚看到是每一步是如何处理的
1.3
怎么学
IL
世上有个定律叫
< br>“
二八定律
”
,
80%
的功能,只要用
20%
的技术就可以完成,但要完
成另外
20%
可能就需要
80%
技术了,对于
I
L
代码也是如此,有
200
多个指令,
我们只
需要用到其
20%
的指令就可以
解决我们
80%
的问题了,所以我不会写太多,只是让大家
能看懂普通的程序代码编译成
IL
代码后就
行了,还有就是要多看,
IL
代码的每一条指令都
是特定的意思,看得多了自然就懂了,当对自己代码有疑问时尝试看看它对应的
IL
代码,
也许你会了解得更多。
二
如何查看
IL
代码
2.1
步骤
1
编写代码并编译通过
2
<
/p>
找到源文件的
obj
文件下的
.exe
文件
3
导
入到
ILDasm
中反编译成
IL
代码
上图
1 -2
步
3
导
入到
ILDasm
中
ILDasm
中图标含义
三
如何读
IL(
大致了解
)
以上步骤完成后我们就可以看到代
码被编译后的
IL
代码,
以下部份将会
对每一条
IL
指
令做详细的解释
C#
代码
1
static
void
Main(
string
[] args)
2
{
3
int
i =
1
;
4
int
j =
2
;
5
int
k =
3
;
6
ine(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 managed
2
{
3
.entrypoint
//
程序入口
4
// Code size
19 (0x13)
5
.maxstack
3
//<
/p>
定义函数代码所用堆栈的最大深度,
也可理解为
< br>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
//<
/p>
加载第一个变量
(压入
Evaluation
Stack
中)
10
IL_0002:
stloc.
0
//
把
赋值给
Call Stack
中第
0
个位置
(V_0)
11
IL_0003:
ldc.i4.
2
//<
/p>
加载第二个变量
(压入
Evaluation
Stack
中)
12
IL_0004:
stloc.
1
//
把
赋值给
Call Stack
中第
1
个位置
(V_1)
13
IL_0005:
ldc.i4.
3
//<
/p>
加载第三个变量
(
压入
Evaluation
Stack
中
)
14
IL_0006:
stloc.
2
//
把
赋值给
Call Stack
中第
2
个位置
(V_2
)
15
16
//
上面代码初始化完成后要开始输
出了,所要把数据从
Call
Stack
中取出
17
18
IL_0007:
ldloc.
0
//
取
Call Stack
中位置为
0
的元素
(V
_0)
的值
(
的值
)
(
相当于
Copy
一份值
Call St
ack
中
V_0
的值。
V_0
本身的值是不变的
)
19
IL_0008:
ldloc.
1
//
取
Call Stack
中位置为
1
的元素
(V
_1)
的值
(
的值
)
(
同上
)
20
IL_0009:
add
//
做加法操作
21
IL_000a:
ldloc.
2
//
取出
Call Stack
中位置为
2
的元素
(V_2)
的值
(
的值
)
22
IL_000b:
add
//
做加法操作
23
IL_000c:
call
void
[mscorlib]e::WriteLine(int32)
//
调
用输出方法
24
IL_0011:
nop
25
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
没有任何操作,我们也不用管它,
ldstr.:
即
Load
String
把字符串加压入
Evaluation
Stack
中
stloc.
:把
Evaluation
Stack
中的值弹出赋值到
Call
Stack
中
ldloc.:
把
Call Stac
k
中指定位置的值取出
(copy)
存
入
Evaluation
Stack
中
以上两条
指令为相互的操作
stloc
赋值,
ldloc
< br>取值
call:
调用指定的方法
ret:
即
return
标记返回
每一句
IL
代码都加了注释后,
是不是觉得
IL
代码其实并不难呢,
因为它的每一条指令
都是固定的,你只要记住了,看
IL
代码就比较轻松了。
四
如何读
I
L(
深入了解
)
4.1
提出问题
有了上面的一点
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(
我理解为托管堆,存储引用类型的
值
)
。
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,<
/p>
当超过
8
时就是一个统一指令
ldc.i4.S
IL_0001: ldc.i4.1
//
加载第一个变量
i
当执行这一条指令时会把变量
i
的值压入
Evaluation
Stack
中做临时存储
IL_0002: stloc.0
//
把
i
赋值给
Call Stack
中第
0
个位置
当执行这一条指信时会把
Evaluation Stack
中的
i
弹出赋值给
Call Stack
中的
第
0
个位
置
IL_0007:
ldloc.0 //
取出
Call Stack
位置为
0
的元素
(i)
当执行这条指令时会将
Call Stack
中的位置为
0
的
元素的值取出
(copy)
压入
Eva
luation
Stack
等待做加法的指令
Add
IL_000b: add //
做加法操作
add
这一操作完成后,会把结果存
在
Evaluation
Stack
中等待下一步的指令操作
4.4
问题回答