由於數組是引用類型,因此數組的處理根據數組元素的類型是否爲「可直接傳遞到非託管代碼」的類型而分爲兩種狀況。主要目標是看內存是怎麼變化的,是複製仍是鎖定。數組
數組中的元素是"可直接傳遞到非託管代碼中"的類型函數
這種類型不少,好比 int double 等。spa
完成的託管代碼和非託管代碼以下:3d
///////////////////////非託管代碼 extern "C" __declspec(dllexport) void DoArray(int a[],int length) { for(int i=0;i<length;i++) { printf("%d\n",a[i]); a[i]=99; } } ////////////////////////////託管代碼 [DllImport(@"C:\Users\Administrator\Desktop\pInvoke\CPPDLL\Debug\CPPDLL.dll")] private static extern void DoArray(int []arr,int length); int[] arr = new int[] {1,2,3,4,5,6,7 }; DoArray(arr,arr.Length);//斷點
foreach(int a in arr)
{
Console.WriteLine(a);
}
跟蹤過程:斷點先下在託管代碼調用函數的地方--DoArray處。這是看一下數組的內存狀況以下,指針
0x01FABAA4 01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00 ................
0x01FABAB4 05 00 00 00 06 00 00 00 07 00 00 00 00 00 00 00 ................code
能夠看到7個數字狀況。地址是0x01fabaa4.記下這個地址,繼續。。。blog
來到非託管代碼中,發現a的地址如圖:內存
能夠看到,和託管代碼中數組的地址是同樣的。再執行,打印結果:字符串
1 2 3 4 5 6 7 99 99 99 99 99 99 99.string
結論:若是數組中的元素是「可直接傳遞到非託管代碼」中的類型時。默認的封送方式是採用了[In,Out]修飾的,傳遞是一個指針,先鎖定內存再傳遞指針,而且非託管代碼對數組的修改會反映到託管代碼中。
數組的元素是「非可直接傳遞到非託管代碼中」的類型
託管內存中數組元素是char類型,是一種「非可直接傳遞到非託管代碼中」的類型。看看內存的狀況吧。
private static extern void DoArray2(char [] arr, int length); static void Main(string[] args) char[] carry = new char[] { 'a','b','c'}; DoArray2(carry, carry.Length); foreach (char c in carry) { Console.Write(c + "\t"); } ////////////////////////非託管代碼 extern "C" __declspec(dllexport) void DoArray2(char carry[],int length) { for(int i=0;i<length;i++) { printf("%c\t",carry[i]); carry[i]='?'; } }
斷點跟蹤,斷在DoArray2(carry,carry.Length)調用處, carry在非託管代碼中的分佈以下:
0x01D4BAB8 61 00 62 00 63 00 00 00 00 00 00 00 00 00 00 00 a.b.c...........
接着斷在非託管代碼領域,數組carry地址顯示爲:
0x004ADC70 61 62 63 00 00 00 3d 00 d8 dd 4a 00 3d 00 22 00 abc...=.??J.=.".
顯然,不是一塊地址,結果可想而知,修改也不會反映到託管內存中了。
結果:a b c a b c .
結論:當數組中的元素是「非可直接傳遞到非託管代碼中」的類型時,這時的封送過程:先在非託管內存申請一塊內存,把託管內存中字符串的數據傳遞複製過去,再把新內存的指針做爲參數傳遞給函數,函數對數組的操做都是基於新內存,結果不會返回到託管代碼中。另外值得一說的是,當調用完成後,非託管內存中的數據沒有被釋放掉。
使用IntPtr來封送數組