在C#中提供了一維數組,多維陣列,和交錯數組(也叫齒狀數組Jagged Array),由於C#本身並不直接支援非0基(0基的意思是數組的最小索引是0)的數組(雖然可以通過Array.CreateInstance()方法在C#中建立非0基數組),而且CLS(通用語言規範)也並不支援非0基數組,所以這篇文章中不討論非0基數組。
1,一維0基數組:
一般情況下,建議大家最好使用一維0基數組,在C#直接支援的數組中,這種類型的數組效能最好,因為在IL中直接提供了特殊的指令來操作一維0基數組,大家看看如下的代碼:
int[] arr = new int[5];
int i = arr[3];
這兩行代碼產生的IL是這樣的:
.entrypoint
// 代碼大小 13 (0xd)
.maxstack 2
.locals init ([0] int32[] arr,
[1] int32 i)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: newarr [mscorlib]System.Int32
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: ldc.i4.3
IL_000a: ldelem.i4
IL_000b: stloc.1
IL_000c: ret
大家可以看到IL提供了newarr指令來建立一個數組,ldelem.i4指令來擷取Int32類型的數組元素,此外IL還6提供了其他的指令來操作一維0基數組:
ldelema :讀取一維0基數組中一個元素的地址
ldlen :讀取一維0基數組中的元素個數
stelem :為數組中的一個元素賦值
ldelem.ref:則用於操作所有包含參考型別對象的數組
2,交錯數組
交錯數組實際上就是數組的數組,也就是說交錯數組中的每個元素本身又是一個數組,每個嵌套數組並不要求長度一致,大家看如下代碼:
int[][] arr = new int[5][];
arr[0] = new int[3];
arr[1] = new int[4];
arr[2] = new int[5];
arr[3] = new int[6];
arr[4] = new int[7];
int i=arr[3][4];
產生的IL代碼如下:
.entrypoint
// 代碼大小 60 (0x3c)
.maxstack 3
.locals init ([0] int32[][] arr,
[1] int32 i)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: newarr int32[]
IL_0007: stloc.0
IL_0008: ldloc.0
IL_0009: ldc.i4.0
IL_000a: ldc.i4.3
IL_000b: newarr [mscorlib]System.Int32
IL_0010: stelem.ref
IL_0011: ldloc.0
IL_0012: ldc.i4.1
IL_0013: ldc.i4.4
IL_0014: newarr [mscorlib]System.Int32
IL_0019: stelem.ref
IL_001a: ldloc.0
IL_001b: ldc.i4.2
IL_001c: ldc.i4.5
IL_001d: newarr [mscorlib]System.Int32
IL_0022: stelem.ref
IL_0023: ldloc.0
IL_0024: ldc.i4.3
IL_0025: ldc.i4.6
IL_0026: newarr [mscorlib]System.Int32
IL_002b: stelem.ref
IL_002c: ldloc.0
IL_002d: ldc.i4.4
IL_002e: ldc.i4.7
IL_002f: newarr [mscorlib]System.Int32
IL_0034: stelem.ref
IL_0035: ldloc.0
IL_0036: ldc.i4.3
IL_0037: ldelem.ref
IL_0038: ldc.i4.4
IL_0039: ldelem.i4
IL_003a: stloc.1
IL_003b: ret
代碼比較長,前面基本都是初始化的代碼,大家不用太關心,但是大家明顯能看到一維0基的交錯數組同樣也可以利用IL為一維0基數組提供的特殊指令,所以效能和一維0基數組相同,唯一的不同是多了一次嵌套數組的訪問!
3,多維陣列:
多維陣列一般大家可能直觀上的認為多維陣列的效能也應該不錯,因為而多維陣列當中,由於用於儲存元素的記憶體空間是連續的,而且數組的每一維元素個數固定,所以可以輕易的根據元素各維的索引值計算出元素在數組記憶體中位移量,比如,一個多維陣列arr[6,7],元素arr[2,4]的位移量是2*7+4,事實證明這種直觀看法並不正確,我們看如下代碼:
int[,] arr = new int[5,6];
int i = arr[3, 4];
產生的IL代碼如下:
.entrypoint
// 代碼大小 19 (0x13)
.maxstack 3
.locals init ([0] int32[0...,0...] arr,
[1] int32 i)
IL_0000: nop
IL_0001: ldc.i4.5
IL_0002: ldc.i4.6
IL_0003: newobj instance void int32[0...,0...]::.ctor(int32,
int32)
IL_0008: stloc.0
IL_0009: ldloc.0
IL_000a: ldc.i4.3
IL_000b: ldc.i4.4
IL_000c: call instance int32 int32[0...,0...]::Get(int32,
int32)
IL_0011: stloc.1
IL_0012: ret
大家都知道,數組是繼承於Array的參考型別,這裡int32[0...,0...]類型就是CLR為多維陣列構造的一個類型,Get(int32,int32)方法是CLR為這個類型構造的一個方法,類似的方法還有:
int Get(int d1, int d2); //擷取特定元素的值
void Set(int d1, int d2, int v); //設定特定元素的值
int* Address(int d1, int d2); //擷取特定元素的地址
函數調用本身是比較耗時的,因為它包含了參數的壓棧出棧,以及程式控制流程的轉移等,顯然,多維陣列的這種通過函數調用來訪問元素的方式沒有一維0基的交錯數組直接使用IL指令的方式快!