【转载】读懂IL代码就这么简单 (一)

【转载】读懂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中                      

<span>【转载】读懂IL代码就这么简单 (一)</span> <span>【转载】读懂IL代码就这么简单 (一)</span>

  ILDasm中图标含义

<span>【转载】读懂IL代码就这么简单 (一)</span>

三  如何读IL(大致了解)

  以上步骤完成后我们就可以看到代码被编译后的IL代码,以下部份将会对每一条IL指令做详细的解释

C#代码 

         static void Main(string[] args)
         {
             int i = 1;
             int j = 2;
             int k = 3;
             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 标记出来,所以自己画了一张图

<span>【转载】读懂IL代码就这么简单 (一)</span>

// 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  3  //定义函数代码所用堆栈的最大深度,也指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.1    //加载第一个变量"i"的值      (压入Evaluation Stack中)  
    IL_0002:  stloc.0     //从栈中把"i"的值弹出并赋值给Record Frame中第0个位置(V_0)   11   IL_0003:  ldc.i4.2    //加载第二个变量"j"的值       (压入Evaluation Stack中) 
    IL_0004:  stloc.1     //从栈中把"j"的值弹出并赋值给Record Frame中第1个位置(V_1)13   IL_0005:  ldc.i4.3    //加载第三个变量"k"的值       (压入Evaluation Stack中)
    IL_0006:  stloc.2     //从栈中把 "k"的值弹出并赋值给Record Frame中第2个位置(V_2)15 
     //上面代码初始化完成后要开始输出了,所以要把数据从Record Frame中取出
  
    IL_0007:  ldloc.0     //取Record Frame中位置为0的元素(V_0)的值("i"的值)并压入栈中  (相当于Copy一份值Call Stack中V_0的值。V_0本身的值是不变的)
    IL_0008:  ldloc.1     //取Record Frame中位置为1的元素(V_1)的值("j"的值)并压入栈中     (同上)
    IL_0009:  add         // 做加法操作
    IL_000a:  ldloc.2     // 取出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中做临时存储

<span>【转载】读懂IL代码就这么简单 (一)</span>



IL_0002:  stloc.0     //把i 赋值给Call Stack中第0个位置
当执行这一条指信时会把Evaluation Stack 中的 i 弹出赋值给Record Frame中的第0个位置

<span>【转载】读懂IL代码就这么简单 (一)</span>



IL_0007:  ldloc.0     //取出Record Frame位置为0的元素 (i)
当执行这条指令时会将 Record Frame中的位置为0的元素的值取出(copy)压入Evaluation Stack 等待做加法的指令 Add

<span>【转载】读懂IL代码就这么简单 (一)</span>

  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的一些基本指令,我会结合我自己的理解,尽量把文字写得通俗一点,让大家更容易理解。

另外本人水平有限,难免会有理解错误的地方,如有发现,请指出!我会马上修改,以免误导他人。

 

【转自】http://www.cnblogs.com/zery/p/3366175.html

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/119356.html原文链接:https://javaforall.net

赞 (0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • linux解压安装包rar_ubuntu rar文件解压

    linux解压安装包rar_ubuntu rar文件解压#wgethttps://www.rarlab.com/rar/rarlinux-x64-5.8.b4.tar.gz—>>下载包#ls-lrtrar/rar/order.htmrar/acknow.txtrar/readme.txtrar/default.sfxrar/license.txtrar/rarfiles.lstrar/…

    2022年10月21日
  • 3.3v与5v电平转换_5v电压经过10位A/D转换

    3.3v与5v电平转换_5v电压经过10位A/D转换问题背景在设计一个带MCU或者ARM系统电路时候,经常遇见MCU的VCC是3.3V,但是外围电路需要5V,有时候是反过来。虽然现在MCU的IO都声称支持TTL电平,但是我们谁也不想将MCU的IO口直接接上5V,即使IO口先串联一个电阻,然后再接上5V,这样总是不放心,担心烧掉MCU。再说了,MCU声称IO口支持TTL电平,但是并不是所有的IO都是这样,反正有隐患。解决方法就是电平转换。具体的我只说我在实际项目中运用到的方法,而且在项目中运用比较方便的,其他的都不再说了。法一:基于门电路第一种:利用O

    2022年8月10日
  • cmd命令如何切换盘符_请输入windows所在盘符

    cmd命令如何切换盘符_请输入windows所在盘符MS-DOS(/ˌɛmˌɛsˈdɒs/MicrosoftDiskOperatingSystem)isanoperatingsystemforx86-basedpersonalcomputersmostlydevelopedbyMicrosoft.1.开始->运行->cmd->Enter2.切换到D盘3.切换到E盘4.切换到C盘5.退出cmdhttps://yongqiang.blog.csdn.net/…

    2022年10月3日
  • java的动态代理机制详解_java为什么是静态语言

    java的动态代理机制详解_java为什么是静态语言说起java动态代理,在我刚开始学java时对这项技术也是十分困惑,明明可以直接调通的对象方法为什么还要使用动态代理?随着学习的不断深入和工作经验的积累,慢慢的体会并理解了java动态代理机制。昨天再给公司新同事做技术培训时有同学就对动态代理产生了疑问,我这里梳理一遍一并记录一下,方便大家查看对自己也是加深记忆。(1)什么是代理?大道理上讲代理是一种软件设计模式,目的地希望能做到代码重用。具体上讲…

    2022年10月9日
  • 正交矩阵的模_正交矩阵的向量组

    正交矩阵的模_正交矩阵的向量组本节主要讲解正交(orthogonal)概念对于向量、基和子空间的意义。1.正交向量Orthogonalvectors2.正交子空间Orthogonalsubspaces3.零空间与行空间正交Nullspaceisperpendiculartorowspace4.矩阵A^TA

    2022年10月22日
  • JBOSS 下载_JbusDriver

    JBOSS 下载_JbusDriver1:JBossApplicationServerDownloadshttp://jbossas.jboss.org/downloads/只下载JBossAS7.1.1.Final ASCertifiedJavaEE6FullProfile 2012-03-09 LGPL [b]Communityparticipationonly[/b]

    2022年10月4日

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号