IL系列文章之三:Array in IL

类别:.NET开发 点击:0 评论:0 推荐:

IL系列文章之三:

Array in IL

         正如题目所言,这一节我们研究IL中的array(数组)。我们将看到如何定义一个数组,如何查询数组元数,使用for语句和foreach语句的异同。

         先来看看我用C#写的一段程序:

 

using System;

class array1

{

         public static void Main ()

         {

                   int [] number;

                   number = new int [6];

                   for(int i = 0;  i < 6; i++)

                   {

                            number[i] = i * i;

                   }

                   foreach(int num in number)

                   {

                            Console.WriteLine(num.ToString());

                   }

         }

}

         这段程序相信大家都看得懂吧(如果看不懂的话最好就别往下看了)。在这段程序中我们看到了C#如何定义一个数组,这和我们以前在C/C++使用的方法不太相同。C#使用了如下两句,来定义一个数组:

                   int [] number;

                   number = new int [6];

而在C/C++中使用一句

                   int number[6];

就够了。产生这种差异的缘由何在呢?别慌,马上就能看到。还有一点,C#中有foreach语句,这个语句与传统的for语句比较起来又如何呢?看下面,是上面的程序用ildasm反编译产生的(为了大家能看得更明白,我对反编译产生的il程序做了一些修改)。

 

.assembly array1{}

 

.class private auto ansi beforefieldinit array1

       extends [mscorlib]System.Object

{

  .method public static void  Main() cil managed

  {

    .entrypoint

    .maxstack  4

    .locals init (int32[] V_0,//int[] number

             int32 V_1,//i

             int32 V_2,

             int32[] V_3,//a pointer of array

             int32 V_4)

                   ldc.i4.6//load int32 const 6(length of the array) onto the stack

                   newarr     [mscorlib]System.Int32//number = new int[6],

//length of the array must be loaded onto the stack before

                   stloc.0//number

                   ldc.i4.0//load first local(V_0) onto the stack

                   stloc.1//int i = 0

                   br.s       loopfor      //goto “loopfor”, “loopfor” is only a lable

         //startfor is a lable too

    startfor:  ldloc.0//number

                   ldloc.1//load V_1 onto the stack

                   ldloc.1

                   ldloc.1

                   mul//multiply

                   stelem.i4//i = i * i

                   ldloc.1

                   ldc.i4.1

                   add

                   stloc.1//i = i + 1

    loopfor:  ldloc.1

                   ldc.i4.6

                   blt.s      startfor//if i(V_1) less than 6, goto “startfor”

 

                   ldloc.0

                   stloc.3//look it!!

                   ldc.i4.0

                   stloc.s    V_4

                   br.s       loopforeach

 

    startforeach:  ldloc.3

                   ldloc.s    V_4

                   ldelem.i4

                   stloc.2

                   ldloca.s   V_2//load address of local_2(V_3)

                   call       instance string [mscorlib]System.Int32::ToString()

                            //cast int to a new string instance and stord it into local_2

                   call       void [mscorlib]System.Console::WriteLine(string)//print it

                   ldloc.s    V_4

                   ldc.i4.1

                   add

                   stloc.s    V_4

    loopforeach:  ldloc.s    V_4//index of V_3[]

                   ldloc.3

                   ldlen

                   conv.i4//conver len to int32

                   blt.s      startforeach

 

                   ret//return, must exist, or else the program will stop here

  } // end of method array1::Main

}

         分析这段程序我们可以看出来定义一个数组的三个步骤,不知你看出了没有?

1.   定义一个数组指针,int32[] V_0

2.   将数组长度移到堆栈顶,ldc.i4.6

3.   为其分配空间,newarr [mscorlib]System.Int32

这下明白了C#中的数组为什么要通过两个步骤来定义了吧。其原因就在于在C#中我们是把数组放到托管堆上,而在C++中使用(int number[6])数组不是放在堆上的。IL能做C#不能做到的事情,如数组的下界可以不从0开始等(下次有机会写个例子J)。

为数组元素赋值,需要四个步骤,

1.     将数组指针移到栈顶,ldloc.0

2.     将数组元素的Index移到栈顶,ldloc.1

3.     将要赋的值移到栈顶(这里是同过mul运算实现),

4.     将值从栈顶移出,赋给元素,stelem.i4

选择数组元素的方法和上面差不多,只是用不着第3步了,第4步的st换成ld

对于for语句和foreach语句其实没有本质的差异。使用foreach语句时,IL会预先产生一个数组指针int32[] V_3,在遍历数组时再把已知数组的指针赋给这个指针,以后的指令就和for语句相差无几了。

        关于一维数组的用法基本上差不多了,下一次我可能要写一些关于多维数组和锯齿数组的内容。

        待续……

本文地址:http://com.8s8s.com/it/it45764.htm