IL系列文章之三:Array in IL

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 语句相差无几了。

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

待续……

Published At
Categories with Web编程
Tagged with
comments powered by Disqus