讀懂IL代碼就這么簡單(二)


一 前言

  IL系列 第一篇寫完后 得到高人指點,及時更正了文章中的錯誤,也使得我寫這篇文章時更加謹慎,自己在了解相關知識點時,也更為細致。個人覺得既然做為文章寫出來,就一定要保證比較高的質量,和正確率 。感謝 @冰麟輕武 的指點

你沒有看第一篇?  點這里看第一篇 讀懂IL代碼就這么簡單(一)  

IL指令大全 :IL指令詳解

IL反編譯工具: ILDasm

知識點回顧:

Managed Heap(托管堆):用於存放引用類型的值

Evaluation Statck(計算棧):臨時存放值類型數據,引用類型地址的堆棧(這個是棧,所以遵循棧的操作特點,先進后出)

Call Stack(調用棧):其中的Record Frame 用於存放.locals init(int32 V_0)指令的參數值如:V_0 (Record Frame是一個局部變量表,所以不遵守FILO原則 )

 

二 指令詳解(基本介紹)

2.1 知識點介紹

  在第一篇時,我只詳細的寫了值類型的IL指令,這一篇會主要以引用類型為主,這一篇會有裝箱操作,所以先寫一下裝箱操作在內存中是如何操作的

裝箱操作:1 內存分配,在托管堆中分配內存空間,2 將值類型的字段拷貝到新分配的內存中,3 將托管堆中的對象地址返回給新的對象

操作過程如下圖

 

C#代碼 

 1         /*
 2          Author:zery-zhang
 3          BlogAddress:http://www.cnblogs.com/zery/
 4          */
 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-zhang
 3         BlogAddress:http://www.cnblogs.com/zery/
 4     */
 5     .method private hidebysig static void  Main(string[] args) cil managed
 6 {
 7   .entrypoint
 8   // Code size       48 (0x30)
 9 
10  //以下代碼 完成 C#代碼中初始化變量的操作
11 
12   //計算棧(Evaluation Stack) 可容納數據項的最大個數
13   .maxstack  2              
14    //定義並初始化參數 並存入 局部變量表(Record Frame)中
15   .locals init (string V_0,int32 V_1) 
16   IL_0000:  nop
17   //把字符串壓入計算棧(Evaluation Stack)中
18   IL_0001:  ldstr      "Zery"   
19   // 從計算棧中彈出("Zery")字符,並賦值給局部變量表中第0個位置的元素V_0
20   IL_0006:  stloc.0       
21   //把整數22壓入計算棧中  
22   IL_0007:  ldc.i4.s   22    
23   //把整數22彈出,並賦值給局部變量表中第1個位置的元素V_1
24   IL_0009:  stloc.1             
25 
26   //以下代碼完成C#中的輸出操作
27 
28   //取出局部變量表中V_1元素的值 "22" (copy)並壓入計算棧中
29   IL_000a:  ldloca.s   V_1 
30   //彈出剛剛壓入的值("22")調用ToString方法轉成string類型並將引用存入計算棧中
31   IL_000c:  call       instance string [mscorlib]System.Int32::ToString() 
32    //取出局部變量表中第0個位置元素(V_0)的值("Zery")壓入計算棧中(此時計算棧中有兩個值,指向推管堆中"22"的引用地址和字符串"Zery")
33   IL_0011:  ldloc.0            
34    //彈出計算棧中兩個值調用String的Concat方法把字個字符拼接存入托管堆中(Managed Heap )並返回地址壓入計算棧中
35   IL_0012:  call       string [mscorlib]System.String::Concat(string,string)   
36    //調用輸出方法,調用輸出方法后計算棧中的值(指向托管堆字符的地址)會被回收 。
37   IL_0017:  call       void [mscorlib]System.Console::WriteLine(string) 
38 
39   //未ToString的操作
40   IL_001c:  nop
41    //取局部變量表中第1個位置的元素V_1的值("22") 壓入計算棧中
42   IL_001d:  ldloc.1              
43    //把剛剛壓入的整數22 裝箱並返回指向托管堆的地址存入計算棧中
44   IL_001e:  box        [mscorlib]System.Int32   
45    //取局部變量表中第0個位置的f元素V_0的值("Zery")並壓入計算棧中
46   IL_0023:  ldloc.0              
47    //彈出計算棧中兩個值調用String的Concat方法把字個字符拼接存入托管堆中(Managed Heap )並返回地址壓入計算棧中
48   IL_0024:  call       string [mscorlib]System.String::Concat(object,object)   
49    //調用輸出方法
50   IL_0029:  call       void [mscorlib]System.Console::WriteLine(string)        
51   IL_002e:  nop
52    //標記返回
53   IL_002f:  ret               
54 } // end of method Program::Main

2.2 IL指令詳解

  .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:  調用指定的方法

  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)

裝箱的過程圖在上面已給出此處只把結果畫出

 

 

 

四 總結

  1 用兩篇把 值類型與引用類型在內存中不同的位置與不同的操作詳細的寫了,我覺得還是很有必要的,因為所有的數據類型由這兩種類型組成,把這兩種類型的操作了解了

看其它IL指令就是透過本質看現象了。

     2  關於畫圖,我覺得畫圖是程序員必學的知識,牛X的程序員畫出來的,系統架構圖,系統設計圖等都是很有結構很清晰的。我的畫圖技能還有太多不足,只是畫這種簡單的圖都覺得,無法完美的表達自己頭腦所想的那樣,  

    

     如果覺得文章與給您帶來一點 收獲 那就 幫忙點個推薦吧,也讓更多的人能關注並了解IL  您的推薦是我源源不斷的寫作力

     如果您希望您的技術路上能有更多的朋友,那就關注一下吧

    注:本人不才,水平有限,如有不對之處,希望能及時提出,我會馬上更正,以免誤導他人 謝謝!

 

   成長在於積累

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM