把Array说透(续一)
1. 寫在前面的
在前文中,我主要介紹了數(shù)組的一些相關(guān)知識(shí),希望加深各位對Array的理解,不過,看過Ivony…同學(xué)的回復(fù),我發(fā)覺自己離說透還有很大的距離,于是就有了下面的文章。在本文中,我也主要來圍繞Ivony…同學(xué)提出的幾點(diǎn)問題來作以說明,問題如下:
A、數(shù)組在托管堆內(nèi)部是怎么存放的?數(shù)組元素的位置是連續(xù)的么?
B、非零基數(shù)組可以和零基數(shù)組轉(zhuǎn)換么?
C、int[]與System.Array的關(guān)系到底是同一類型?還是基類與派生類的關(guān)系?
D、ldelem不檢查下標(biāo)越界么?
E、多維數(shù)組可以和零基數(shù)組轉(zhuǎn)換么?
F、Array.Copy和CopyTo與手動(dòng)拷貝性能有多大差距?
G、數(shù)組的協(xié)變是怎么做到的?
H、數(shù)組是如何實(shí)現(xiàn)泛型接口(如IList<T>)的?
I、多維數(shù)組每一維度長度必須相等么?必須零基么?
J、數(shù)組的Length屬性到底指示的是什么?
2. 數(shù)組內(nèi)存詳解
在這里,我們依然把數(shù)組分為零基數(shù)組和非零基數(shù)組來討論。
首先來看零基數(shù)組的內(nèi)存分配,廢話少說,我們先來看測試代碼:
static unsafe void Main(string[] args) {int[] intArr = new int[3];intArr[0] = 1;intArr[1] = 2;intArr[2] = 3; }代碼本身很簡單,接下來單步執(zhí)行向下看,首先我們來查看一下源代碼的匯編代碼:
int[] intArr = new int[3]; 00000035 mov edx,3 0000003a mov ecx,61CD4192h 0000003f call FFFB2140 00000044 mov dword ptr [ebp-44h],eax 00000047 mov eax,dword ptr [ebp-44h] 0000004a mov dword ptr [ebp-40h],eax intArr[0] = 1; 0000004d mov eax,dword ptr [ebp-40h] 00000050 cmp dword ptr [eax+4],0 00000054 ja 0000005B 00000056 call 624B6B29 0000005b mov dword ptr [eax+8],1 intArr[1] = 2; 00000062 mov eax,dword ptr [ebp-40h] 00000065 cmp dword ptr [eax+4],1 00000069 ja 00000070 0000006b call 624B6B29 00000070 mov dword ptr [eax+0Ch],2 intArr[2] = 3; 00000077 mov eax,dword ptr [ebp-40h] 0000007a cmp dword ptr [eax+4],2 0000007e ja 00000085 00000080 call 624B6B29 00000085 mov dword ptr [eax+10h],3 }在這里,我們就可以清晰地發(fā)現(xiàn),在0x0000005b,0x00000070和0x00000085中,mov操作的目標(biāo)地址之間是相隔4個(gè)Bytes的,也就是一個(gè)整數(shù)位。接下來我們來進(jìn)一步證實(shí)。
當(dāng)我們?yōu)閿?shù)組分配過內(nèi)存地址后,打開即使窗口查看數(shù)組所在的內(nèi)存地址。
接下來打開內(nèi)存窗口還查看0x015cc790內(nèi)存塊的數(shù)據(jù):
以上是對數(shù)組賦值前的情況,賦值后的內(nèi)存數(shù)據(jù)如下:
在這里可以更清晰地看出,數(shù)組元素之間差的正好是4個(gè)Bytes,也就是一個(gè)整數(shù)位。由此,我們可以得出結(jié)論。零基數(shù)組的元素在內(nèi)存中是連續(xù)排布的。
接下來我們來看一下非零基數(shù)組:
由于空間所限,過程如上,就不再發(fā),截圖證明:
總之,當(dāng)我們在托管堆中為數(shù)組分配內(nèi)存時(shí),數(shù)組占據(jù)一段連續(xù)的內(nèi)存空間。
我們知道,當(dāng)我們在托管堆中初始化一個(gè)對象時(shí),每個(gè)對象都需要維護(hù)一個(gè)指針,該指針的作用是指向下一塊空閑內(nèi)存空間,由于對數(shù)組的操作經(jīng)常是循環(huán)遍歷等操作,這樣如果把數(shù)組分配到一個(gè)連續(xù)的內(nèi)存空間有一下兩個(gè)好處:
A. 減少內(nèi)存碎片
B. 節(jié)省內(nèi)存,不需要維護(hù)指針
C. 基地址不需要發(fā)生變化,只需要改變偏移量即可,在一定程度上也提高了訪問的效率。
接下來,我們還需要來補(bǔ)充一下數(shù)組在棧上分配內(nèi)存的情況:
還記得上文中提到的這個(gè)關(guān)鍵字吧,stackalloc,就是他了。補(bǔ)充一下,在上文的回復(fù)中,有人問到說??臻g上分配的內(nèi)存是不是也被垃圾回收器回收?這里的??臻g和C語言中的棧一樣,沒有垃圾回收器,每個(gè)變量都有他自己的作用域,當(dāng)出了作用域后,變量自動(dòng)銷毀,具體的函數(shù)執(zhí)行過程,請參看《深入理解計(jì)算機(jī)系統(tǒng)》。
3. 再論零基數(shù)組和非零基數(shù)組
我們先來看這樣一段代碼:
static void Main(string[] args) {int[,] intArr = (int[,])(Array.CreateInstance(typeof(Int32), new int[] { 3,4 }, new int[] { 1,1 }));intArr[2, 3] = 1; }這段代碼沒有問題,我們將Array顯式地轉(zhuǎn)換成了強(qiáng)類型的二維數(shù)組,然后直接訪問索引對其復(fù)制。
但是我們知道,對弱類型的Array而言,我們不能通過其下標(biāo)訪問他的元素,而只能通過SetValue和GetValue來獲得值,但是我們看到SetValue和GetValue訪問和設(shè)置的值的類型都是Object,這就意味著我們需要對其進(jìn)行一次裝箱或者拆箱。那么我們有沒有辦法也生成一個(gè)強(qiáng)類型的非零基數(shù)組呢?
在上文中,我們提到過,.NET Framework的幾種數(shù)組類型:
一維零基數(shù)組:System.Int32[]。一維非零基數(shù)組:System.Int32[*]。多維數(shù)組:System.Int32[,]。
那么也就是說,我們是否能通過這樣的代碼來把Array轉(zhuǎn)換成一維非零基數(shù)組呢?
static void Main(string[] args) {int[*] intArr = (int[*])(Array.CreateInstance(typeof(Int32), new int[] { 3}, new int[] { 1 })); }事實(shí)證明是錯(cuò)誤的。在CLR via C#中Jeffery有這樣一段話:
“C# does not allow you to declare a variable of type string[*],and therefore it is not possible to user C# syntax to access a single-dimensional ,non-zero-based array.”
這段話翻譯成中文的意思就是:C#不允許聲明一個(gè)string[*]類型的變量,因此,我們能夠使用C#語法來訪問一個(gè)非零基一維數(shù)組。
通過以上的解釋,我們也許又額外明白了一點(diǎn),Array究竟是數(shù)組類型,還是數(shù)組類型的基類?
通過上面的一些代碼,我們不妨又把數(shù)組重新分類為“強(qiáng)類型數(shù)組”和“弱類型數(shù)組”。而Array就屬于弱類型數(shù)組。為什么Array是所有數(shù)組類型的基類,我沒想出辦法來如何證明,只是看到Jeffery說了這樣一句話:
“All Arrays are Implicitly Derived from System.Array”。
我想這句話可以說明問題了,不過還是希望各位大俠指點(diǎn)如果證明這一點(diǎn)。
未完持續(xù)…………
?
轉(zhuǎn)載于:https://www.cnblogs.com/kym/archive/2009/10/09/1579958.html
總結(jié)
以上是生活随笔為你收集整理的把Array说透(续一)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: z-wave
- 下一篇: C#类中操作UI层控件状态[原]