Zery:读懂IL代码就这么简单 二

Zery

发表于2013-10-28 10:33:40

一、前言:

  IL系列 第一篇写完后 得到高人指点,及时更正了文章中的错误,也使得我写这篇文章时更加谨慎,自己在了解相关知识点时,也更为细致。个人觉得既然做为文章写出来,就一定要保证比较高的质量,和正确率 。感谢 冰麟轻武 的指点

知识点回顾:

Managed Heap(托管堆):用于存放引用类型的值

Evaluation Statck(评估栈):临时存放值类型数据,引用类型地址的堆栈(这个是栈,所以遵循栈的操作特点,先进后出)

Call Stack(局部变量列表):,用于存放.locals init(int32 V_0)指令的参数值如:V_0 (不要被Call Stack 这个名字所误导了,它并不是一个栈)

二、指令详解(基本介绍):

  在第一篇时,我只详细的写了值类型的IL指令,这一篇会主要以引用类型为主,这一篇会有装箱操作,所以先写一下装箱操作在内存中是如何操作的

装箱操作:1 内存分配,在托管堆中分配内存空间,2 将值类型的字段拷贝到新分配的内存中,3 将托管堆中的对象地址返回给新的对象

操作过程如下图

C#代码 

/* Author:zery-zhang BlogAddress:http://www.cnblogs.com/zery/ */ static void Main(string[] args) { string name = "Zery"; int age = 22; Console.WriteLine(age.ToString() + name);//已ToString的操作 Console.WriteLine(age+name);//未ToString操作 }

IL代码 

/* Author:zery-zhang BlogAddress:http://www.cnblogs.com/zery/ */ .method private hidebysig static void Main(string[] args) cil managed { .entrypoint // Code size 48 (0x30) //以下代码 完成 C#代码中初始化变量的操作 //评估栈(Evaluation Stack) 可容纳数据项的最大个数 .maxstack 2 //定义并初始化参数 并存入 局部变量表(Call Stack)中 .locals init (string V_0,int32 V_1) IL_0000: nop //把字符串压入评估栈(Evaluation Stack)中 IL_0001: ldstr "Zery" // 从评估栈中弹出("Zery")字符,并赋值给局部变量表中第0个位置的元素V_0 IL_0006: stloc.0 //把整数22压入评估栈中 IL_0007: ldc.i4.s 22 //把整数22弹出,并赋值给局部变量表中第1个位置的元素V_1 IL_0009: stloc.1 //以下代码完成C#中的输出操作 //取出局部变量表中V_1元素的值 "22" (copy)并压入评估栈中 IL_000a: ldloca.s V_1 //弹出刚刚压入的值("22")调用ToString方法转成string类型并将引用存入评估栈中 IL_000c: call instance string [mscorlib]System.Int32::ToString() //取出局部变量表中第0个位置元素(V_0)的值("Zery")压入评估栈中(此时评估栈中有两个值,指向推管堆中"22"的引用地址和字符串"Zery") IL_0011: ldloc.0 //弹出评估栈中两个值调用String的Concat方法把字个字符拼接存入托管堆中(Managed Heap )并返回地址压入评估栈中 IL_0012: call string [mscorlib]System.String::Concat(string,string) //调用输出方法,调用输出方法后评估栈中的值(指向托管堆字符的地址)会被回收 。 IL_0017: call void [mscorlib]System.Console::WriteLine(string) //未ToString的操作 IL_001c: nop //取局部变量表中第1个位置的元素V_1的值("22") 压入评估栈中 IL_001d: ldloc.1 //把刚刚压入的整数22 装箱并返回指向托管堆的地址存入评估栈中 IL_001e: box [mscorlib]System.Int32 //取局部变量表中第0个位置的f元素V_0的值("Zery")并压入评估栈中 IL_0023: ldloc.0 //弹出评估栈中两个值调用String的Concat方法把字个字符拼接存入托管堆中(Managed Heap )并返回地址压入评估栈中 IL_0024: call string [mscorlib]System.String::Concat(object,object) //调用输出方法 IL_0029: call void [mscorlib]System.Console::WriteLine(string) IL_002e: nop //标记返回 IL_002f: ret } // end of method Program::Main

2.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 Stack中

  stloc.:把Evaluation Stack中的值弹出赋值到Call Stack中

  ldloc.:把Call Stack中指定位置的值取出(copy)存入 Evaluation Stack中   以上两条指令为相互的操作stloc赋值,ldloc取值

  call:  调用指定的方法

  box:执行装箱操作

  ret: 即return  标记返回

二、指令详解(深入介绍):

如果看代码中的注释你还不是很理解,那就看看下面的图解过程吧,如果每一步都画图,那工程太大了,所以我会把简单的的步组合成一张图并做上注释

先画初始化的代码详解图  注:为了减少图片所以栈的弹出与压入操作就省去了,都只画出了结果

 

IL_0001: ldstr "Zery"
IL_0006: stloc.0
IL_0007: ldc.i4.s 22
IL_0009: stloc.1

因为字符串是引用类型,所以是保存在托管堆中,而栈中只保存对字符引用的地址,可以看到图中的字符串是在托管中的,而评估栈中只保存了引用

 

IL_000a: ldloca.s V_1
IL_000c: call instance string [mscorlib]System.Int32::ToString()
IL_0011: ldloc.0
IL_0012: call string [mscorlib]System.String::Concat(string,string)
IL_0017: call void [mscorlib]System.Console::WriteLine(string)

IL_001d: ldloc.1
IL_001e: box [mscorlib]System.Int32
IL_0023: ldloc.0
IL_0024: call string [mscorlib]System.String::Concat(object,
object)
IL_0029: call void [mscorlib]System.Console::WriteLine(string)

装箱的过程图在上面已给出此处只把结果画出

 

 

作者:zery 原文地址:http://www.cnblogs.com/zery/p/3366175.html