看懂IL代码

//示例一:输出整数的立方值。
private void PrintCube( int i )
{
    int cube = i * i * i;
    Console.WriteLine( cube );
}
//方法签名。
/**//// hidebysig:MethodAttributes 枚举值之一,指示此方法按名称和签名隐藏,否则只
/// 按名称隐藏。
/// cil managed:未查到具体资料,应是“受中间语言管理”之意。

.method private hidebysig instance void 
          PrintCube(int32 i) cil managed
  {
    // 代码大小       15 (0xf)
    .maxstack  2
    /**//**//**//// 在 .locals 部分声明所有的局部变量。
    .locals init ([0] int32 cube)    /**//**//**//// 第一个名局部变量,int 型,名为 cube。索
                                    /// 引从 0 开始。
    IL_0000:  nop    /**//**//**//// no operation.
    IL_0001:  ldarg.1    /**//**//**//// load argument 第一个方法参数入栈,比如“3”。索引号
                        /// 从 1 开始,而不是从 0 开始。
    IL_0002:  ldarg.1    /**//**//**//// 再次向堆栈压入第一个方法参数,又一个“3”。
    IL_0003:  mul    /**//**//**//// multiply 计算堆栈最顶上两个数的乘积 3×3,并把结果入栈,
                    /// 即堆栈最顶部是 9 了。
    IL_0004:  ldarg.1    /**//**//**//// 再次压入第一个方法参数“3”。
    IL_0005:  mul    /**//**//**//// 堆栈最顶上是“3”,第二是“9”,计算 3×9,此时 27 入栈。
    IL_0006:  stloc.0    /**//**//**//// pop value from stack to local variable 堆栈最顶上的
                        /// 值“27”出栈,并被赋给索引位置“0”处的局部变量 cube,
                        /// 即内存中变量 cube 的值为“27”。
    IL_0007:  ldloc.0    /**//**//**//// 局部变量 cube 的值“27”入栈。
    IL_0008:  call       void [mscorlib]System.Console::WriteLine(int32)
                        /**//**//**//// 控制台输出堆栈最顶上的 32 位整数“27”。
    IL_000d:  nop    /**//**//**//// no operation.
    IL_000e:  ret    /**//**//**//// return from method.
  } // end of method Program::PrintCube
//示例二:把字符串拆分成字符,并按顺序每行输出一个字符
public void SeparateString( string source )
{
    if( source == null )
        return;

    int count = source.Length;

    char c;
    for( int i = 0; i < count; i++ )
    {
        c = source[ i ];
        Console.WriteLine( c );
    }
}

.method public hidebysig instance void 
          SeparateString(string source) cil managed
  {
    // 代码大小       55 (0x37)
    .maxstack  2
    .locals init ([0] int32 count,
             [1] char c,
             [2] int32 i,
             [3] bool CS$4$0000)    /**//**//**//// 索引为“3”的这个布尔型局部变量在 C# 代
                                    /// 码中并未显式声明,是编译器编译时添加的,
                                    /// 用于保存执行过程中布尔运算的结果,比如比
                                    /// 较 source 是否为空时,以及比较 i<count 时。
    IL_0000:  nop
    IL_0001:  ldarg.1    /**//**//**//// 方法参数 source 的值入栈。
    IL_0002:  ldnull    /**//**//**//// “空引用”null入栈。
    IL_0003:  ceq    /**//**//**//// compare equal 比较栈顶的 null 和第二项的 source 是否相等,并
                    /// 把结果 0(false,source 不为空)或 1(true,source 为空)入栈。
    IL_0005:  ldc.i4.0    /**//**//**//// 32 位整型数“0”入栈。
    IL_0006:  ceq    /**//**//**//// 比较栈顶的“0”和堆栈的第二项,第二项可能是“0”,也可能
                    /// 是“1”。比较的结果“1”或“0”入栈。
    IL_0008:  stloc.3    /**//**//**//// 栈顶的“1”或“0”出栈,被保存到索引为“3”的局部变量中。
    IL_0009:  ldloc.3    /**//**//**//// 执行后,栈顶为“1”(source 不为空)或“0”(source 为空)。
    IL_000a:  brtrue.s   IL_000e    /**//**//**//// branch on non-false or non-null 判断栈顶是否
                                    /// 为“1”,如果是,跳转到第“IL_000e”行;否则
                                    /// 继续往下执行。

    IL_000c:  br.s       IL_0036    /**//**//**//// unconditional branch 当栈顶为“0”时,才会
                                    /// 执行到这一行,这一行的执行结果是程序无条件
                                    /// 跳转到第“IL_0036”行。

    IL_000e:  ldarg.1
    IL_000f:  callvirt   instance int32 [mscorlib]System.String::get_Length()    
                        /**//**//**//// 对堆栈最顶上的字符串调用其获取长度的实例方法,长度值被入栈。
                        /// “get_Length()”实际是字符串 Length 属性的“get”部分。
    IL_0014:  stloc.0    /**//**//**//// 局部变量 count 被赋值为字符串长度。
    IL_0015:  ldc.i4.0
    IL_0016:  stloc.2    /**//**//**//// 局部变量 i 被赋值为 0。
    IL_0017:  br.s       IL_002e    /**//**//**//// 无条件跳转到第“IL_002e”行。

    IL_0019:  nop
    IL_001a:  ldarg.1
    IL_001b:  ldloc.2
    IL_001c:  callvirt   instance char [mscorlib]System.String::get_Chars(int32)    
                        /**//**//**//// source 中索引为 i 处的 char 值入栈。
    IL_0021:  stloc.1
    IL_0022:  ldloc.1
    IL_0023:  call       void [mscorlib]System.Console::WriteLine(char)    /**//**//**//// char 值被输
                                                                        /// 出到控制台。
    IL_0028:  nop
    IL_0029:  nop
    IL_002a:  ldloc.2    /**//**//**//// i 值入栈。
    IL_002b:  ldc.i4.1    /**//**//**//// 32 位整数 1 入栈。
    IL_002c:  add    /**//**//**//// i+1 的结果入栈。
    IL_002d:  stloc.2    /**//**//**//// i=i+1。
    IL_002e:  ldloc.2    /**//**//**//// i 值入栈。
    IL_002f:  ldloc.0    /**//**//**//// count 值入栈。
    IL_0030:  clt    /**//**//**//// compare less than 比较 i<count 是否为真,比较结果入栈。
    IL_0032:  stloc.3
    IL_0033:  ldloc.3
    IL_0034:  brtrue.s   IL_0019    /**//**//**//// 如果 i<count 则跳转到第“IL_0019”行。

    IL_0036:  ret
  } // end of method Program::SeparateString


总结分析:

ldstr: 将对字符串的对象引用推送到堆栈上
 ldloc: 将变量的对象引用推送到堆栈上 
 idc.i4   idc.i8:   将4个字节的整数送到堆栈上,将超过32位的常数送到堆栈上
 stloc index: 从堆栈中弹出值并将其放到局部变量index中,index 从0向上编号
 newobj:初始化,调用构造函数,分配内存
 call,callvirt: call 根据引用类型的静态类型来调度方法,callvirt根据引用类型的动态类型来调度方法

 .ctor:构造函数,在类被实例化时,它会被自动调用。

 

 Example 1

class Program           
{         
  static void Main(string[] args)
            {           

       String s = "a";        
                  s = "abcd";           
            }         
}

 IL_0001: ldstr     "a"                   使用ldstr指令为字串对象“a”分配内存,并将此对象引用压入到线程堆栈中
  IL_0006:  stloc.0                           使用stloc指令从线程堆栈顶弹出先前压入的对象引用,将其传给局部变量s(其索引号为0)。
  IL_0007:  ldstr      "abcd"               使用ldstr指令为字串对象“abcd”分配内存,并将此对象引用压入到线程堆栈中
  IL_000c:  stloc.0                            使用stloc指令从线程堆栈顶弹出先前压入的对象引用,将其传给局部变量s(其索引号为0)。

 

 Example 2
(1)String s1 = "ab";          
        s1+="cd";
(2)String s1="ab"+"cd";
 
(1)
         IL_0001: ldstr     "ab"           
         IL_0006:  stloc.0
         IL_0007:  ldloc.0
         IL_0008:  ldstr      "cd"
         IL_000d:  call    string [mscorlib]System.String::Concat(string, string)
         IL_0012:  stloc.0
 (2)
        IL_0001: ldstr     "abcd"
         IL_0006:  stloc.0

可以很清楚地看到,第(1)段代码将导致String类的Concat()方法被调用(实现字串加法运算)。对于第(2)段代码,由于C#编译器聪明地在编译时直接将两个字串合并为一个字串字面量,所以程序运行时CLR只调用一次ldstr指令就完成了所有工作,其执行速度谁快就不言而喻了!

posted @ 2011-10-27 16:55  szjdw  阅读(396)  评论(0编辑  收藏  举报