了解IL代码

一、求和

.net代码:

        private void buttonSunny_Click_1(object sender, RoutedEventArgs e)
        {
            byte a = 1;
            short b = 20;
            int c = 300;
            long d = 4000;
            long lng = a + b + c + d;
            Console.WriteLine(lng);
        }
上面是一段简单的wpf代码,定义5个变量,类型分别为byte、short、int、long,然后求和,最后输出求和的值。

我们带着几个问题再去看下面的IL代码:

1、数值1、20、300、4000分别是以什么类型存储到内存中的,它们都用十六进制表示吗,在被赋值给变量的时候它们发生了什么转换?

2、在求和的过程中,数据又发生了哪些转换,是在哪几步发生转换的?

IL代码:

/*
   Author:Sunny906
   Date:2014/10/29
*/
.method private hidebysig instance void  buttonSunny_Click_1(object sender,
                                                        class [PresentationCore]System.Windows.RoutedEventArgs e) cil managed
{
  // 代码大小       36 (0x24)
  .maxstack  2                  //加载到Evaluation Stack(评估栈,以下简称栈)中的项的最大数目,这并不是说明只能加载2个参数到栈中,而是说一次最多只能加载2个参数到栈中
  .locals init ([0] uint8 a,
           [1] int16 b,
           [2] int32 c,
           [3] int64 d,
           [4] int64 lng)       //初始化方法里所有的局部变量,此处定义了5个变量,变量从0开始按索引存取
  IL_0000:  ldc.i4.1            //入栈,把整数1(int32)加载到栈顶
  IL_0001:  stloc.0             //出栈,取出栈中的栈顶元素1(int32),并赋值给索引为0的局部变量,即a=1
  IL_0002:  ldc.i4.s   20       //入栈,把单字节类型的数值20(int8)加载到栈顶
  IL_0004:  stloc.1             //出栈,取出栈中的栈顶元素20(int32),并赋值给索引为1的局部变量,即b=20
  IL_0005:  ldc.i4     0x12c    //入栈,把整数300(int32)加载到栈顶,其值以16进制存储
  IL_000a:  stloc.2             //出栈,取出栈中的栈顶元素300(int32),并赋值给索引为2的局部变量,即c=300
  IL_000b:  ldc.i4     0xfa0    //入栈,把整数4000(int32)加载到栈顶,其值以16进制存储
  IL_0010:  conv.i8             //整数4000(int32)出栈,把4000转换为int64,将4000(int64)加载到栈顶
  IL_0011:  stloc.3             //出栈,取出栈中的栈顶元素4000(int32),并赋值给索引为3的局部变量,即d=4000
  IL_0012:  ldloc.0             //入栈,把索引为0的局部变量加载到栈顶,即把变量a加载到栈顶
  IL_0013:  ldloc.1             //入栈,把索引为1的局部变量加载到栈顶,即把变量b加载到栈顶
  IL_0014:  add                 //变量b和变量a出栈,两个值相加(a+b),并将结果21(int32)加载到栈顶
  IL_0015:  ldloc.2             //入栈,把索引为2的局部变量加载到栈顶,即把变量c加载到栈顶
  IL_0016:  add                 //变量c和整数21(int32)出栈,两个值相加(21+c),并将结果321(int32)加载到栈顶
  IL_0017:  conv.i8             //整数321(int32)出栈,把321转换为int64,将321(int64)加载到栈顶
  IL_0018:  ldloc.3             //入栈,把索引为3的局部变量加载到栈顶,即把变量d加载到栈顶
  IL_0019:  add                 //变量d和整数321(int64)出栈,两个值相加(321+d),并将结果4321(int64)加载到栈顶
  IL_001a:  stloc.s    lng      //出栈,取出栈中的栈顶元素4321(int64),并赋值给局部变量列表中的lng,即lng=4321
  IL_001c:  ldloc.s    lng      //入栈,把局部变量列表中的lng加载到栈顶,即把变量lng加载到栈顶
  IL_001e:  call       void [mscorlib]System.Console::WriteLine(int64)  //变量lng出栈,调用由传递的方法说明符指示的方法,即WriteLine(int64)
  IL_0023:  ret                 //从当前方法返回
} // end of method MainWindow::buttonSunny_Click_1

方法体里的IL语法说明都加的有注释,下面对方法的头部的IL指令解释下:

.method:表示这是一个方法,名称为buttonSunny_Click_1,无返回值,有两个参数,类型分别是object和class;PresentationCore为程序集;

private:访问权限,该方法被修饰为私有成员;

hidebysig: hide by name-and-signature,方法按名称和签名隐藏,即如果父类和派生类的方法具有相同的名称和签名,父类的方法就会在派生类中被隐藏。

参考stackoverflow

From ECMA 335, section 8.10.4 of partition 1:

    The CTS provides independent control over both the names that are visible from a base type (hiding) and the sharing of layout slots in the 
derived class (overriding). Hiding is controlled by marking a member in the derived class as either hide by name or hide by name-and-signature. 
Hiding is always performed based on the kind of member, that is, derived field names can hide base field names, but not method names, property 
names, or event names. If a derived member is marked hide by name, then members of the same kind in the base class with the same name are not 
visible in the derived class; if the member is marked hide by name-andsignature then only a member of the same kind with exactly the same name 
and type (for fields) or method signature (for methods) is hidden from the derived class. Implementation of the distinction between these two 
forms of hiding is provided entirely by source language compilers and the reflection library; it has no direct impact on the VES itself.

(It's not immediately clear from that, but <code>hidebysig</code> means "hide by name-and-signature".)

Also in section 15.4.2.2 of partition 2:

    hidebysig is supplied for the use of tools and is ignored by the VES. It specifies that the declared method hides all methods of the base 
class types that have a matching method signature; when omitted, the method should hide all methods of the same name, regardless of the signature.

instance:表示该方法被一个实例调用,即方法与对象关联。对应到上面的wpf代码,可以这样调用this.buttonSunny_Click_1(null, new RoutedEventArgs());

cil managed:提示编译器该段代码为托管代码。

现在,我们回到上面的问题:

1、先了解一下num值在入栈的时候的存储类型:

(1) ldc.i4 num将整数值num作为int32入栈;

(2) ldc.i4.numnum值为0~8,将整数值num作为int32入栈;

(3) ldc.i4.s num:num值为-2^7~2^7-1,即-128~127,将整数值num作为int8入栈

更多指令请参考msdn

可以看出,只有[-128,127]之间的整数是以十进制表示的,其他整数则是以十六进制表示;

4000在以int32入栈之后被转换为int64,再出栈并被赋值给变量d。

2、分析上面的IL代码就可以看出,a+b+c的值321发生了转换,由int32转换为int64,以满足add指令;对应IL_0017行。

二、循环

.net代码:

        private void buttonSunny_Click_1(object sender, RoutedEventArgs e)
        {
            for (int i = 0; i < 10; i++)
            {
                if (i % 2 == 0)
                {
                    Console.WriteLine(i);
                }
            }
        }

IL代码:

/*
   Author:Sunny906
   Date:2014/10/29
*/
.method private hidebysig instance void  buttonSunny_Click_1(object sender,
                                                        class [PresentationCore]System.Windows.RoutedEventArgs e) cil managed
{
  // 代码大小       25 (0x19)
  .maxstack  2                  //加载到Evaluation Stack(评估栈,以下简称栈)中的项的最大数目,这并不是说明只能加载2个参数到栈中,而是说一次最多只能加载2个参数到栈中
  .locals init ([0] int32 i)    //初始化方法里所有的局部变量,此处定义了一个int32类型的变量i,索引为0,变量从0开始按索引存取 
  IL_0000:  ldc.i4.0	        //入栈,把整数0(int32)加载到栈顶
  IL_0001:  stloc.0	        //出栈,取出栈中的栈顶元素0(int32),并赋值给索引为0的局部变量,即i=0
  IL_0002:  br.s       IL_0013  //无条件地将控制转移到IL_0013
  IL_0004:  ldloc.0             //入栈,把索引为0的局部变量加载到栈顶,即把变量i加载到栈顶
  IL_0005:  ldc.i4.2	        //入栈,把整数2(int32)加载到栈顶
  IL_0006:  rem                 //整数2(int32)和变量i出栈,计算i div 2的余数,即i%2,并把结果加载到栈顶
  IL_0007:  brtrue.s   IL_000f  //如果栈顶的元素非0,则将控制转移到IL_000f;否则继续向下执行
  IL_0009:  ldloc.0             //入栈,把索引为0的局部变量加载到栈顶,即把变量i加载到栈顶
  IL_000a:  call       void [mscorlib]System.Console::WriteLine(int32) //变量i出栈,调用由传递的方法说明符指示的方法,即WriteLine(int32)
  IL_000f:  ldloc.0             //入栈,把索引为0的局部变量加载到栈顶,即把变量i加载到栈顶
  IL_0010:  ldc.i4.1	        //入栈,把整数1(int32)加载到栈顶
  IL_0011:  add                 //整数1(int32)和变量i出栈,两个值相加,并把结果(i+1)加载到栈顶
  IL_0012:  stloc.0             //出栈,取出栈顶元素,并赋值给索引为0的局部变量,即i=i+1
  IL_0013:  ldloc.0             //入栈,把索引为0的局部变量加载到栈顶,即把变量i加载到栈顶
  IL_0014:  ldc.i4.s   10       //入栈,把单字节类型的整数10(int8)加载到栈顶
  IL_0016:  blt.s      IL_0004  //整数10(int8)和变量i出栈,如果变量i的值小于整数10(int8),则将控制转移到IL_0004
  IL_0018:  ret                 //从当前方法返回
} // end of method MainWindow::buttonSunny_Click_1


  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值