自從有了動態數組,鏈表除了在教科書裏出現外,已經很少在實際編程中被使用了,事實也是如此,數組的確比傳統鏈表快得多,而且也方便的多。
從 Delphi4起,開始了內建各種類型的動態數組支持。但是,對我們來說動態數組支持似乎做的不夠徹底,因爲Delphi竟然連刪除、插入、移動連續元素的函數都沒有提供,讓人使用起來總覺得不夠爽!!! J 。作爲一名程序員,我們當然要有自己解決問題的能力,下面就讓我們簡單介紹一下Delphi 下的動態數組。
在Delphi中,數組類型有靜態數組(a : array[0..1024] of integer)、動態數組(var a : array of integer)、指針數組(即指向靜態數組的指針)和開放數組(僅用於參數傳遞)。靜態數組、指針數組有速度快的好處,動態數組有大小可變的優勢,權衡之下就有了折衷的辦法,那就是定義的動態數組在必要時轉換爲指針。
動態數組聲明之後,只有下面幾個函數可供操作:
1. 設置數組大小,可以任意縮減或增加數組大小
Procedure SetLength(var S ; NewLength : integer);
2. 取出連續元素,複製給另一個數組變量
Function Copy(s;Index,Count : integer) : array ;
3. 取得數組大小及上下限
Function Length(s):integer;
Function High(x):integer;
Function Low(x):integer;
值得注意的是,不加const或var修飾的動態數組會被作爲形參傳遞,而動態數組用const修飾並不意味着你不能修改數組裏的元素(不信你可以字自己在程序中試試。還有一點是High函數調用了Length 函數,所以我們在獲取數組上限時最好直接用 Length(s) 函數。
動態數組在內存空間中佔用4個字節. 動態數組在內存中的分配表如下:
偏移量 內容
-8 32-bit 引用計數
-4 32-bit 數組長度
0..數組長度 * (元素尺寸) - 1 數組元素 元素尺寸=Sizeof(元素類型)
根據上面的分配情況,可以得到如下結果:
如果我們想要清空一個動態數組只需要把“數組長度”和“引用計數”清空即可。”引用上面的一句話就是:“權衡之下就有了折衷的辦法,那就是定義的動態數組在必要時轉換爲指針。”下面是清空動態數組的函數:
procedure DynArraySetZero(var A);
var
P: PLongint; //佔用4個字節,正好符合 32 位內存排列
begin
P := PLongint(A); // 指向 A 的地址
Dec(P); //P 地址偏移量是 sizeof(A),指向了數組長度
P^ := 0; // 長度清空
Dec(P); // 指向引用計數
P^ := 0; //計數清空。
end;
上面的函數就這麼簡單,而且效率也非常高。
下面讓我們再來看看怎樣刪除動態數組中的元素,函數體如下:
{************************************
A 變量類型 , elSize = SizeOf(A)
index 開始刪除的位置索引 ,Count 刪除的數量
****************************************}
procedure DynArrayDelete(var A; elSize: Longint; index, Count: Integer);
var
len, MaxDelete: Integer;
P : PLongint; //4 個字節的長整形指針
begin
P := PLongint(A);// 取的 A 的地址
if P = nil then
Exit;
{
下面這句完全等同於 Dec(P) ; len := P^ 因爲 Dec(P) = Pchar(P) – 4 同樣是移動4 字節的偏移量,只不過後者按字節來移動 }
len := PLongint(PChar(P) - 4)^; // 變量的長度 ,偏移量 -4
if index >= len then //要刪除的位置超出範圍,退出
Exit;
MaxDelete := len - index; // 最多刪除的數量
Count := Min(Count, MaxDelete); // 取得一個較小值
if Count = 0 then // 不要求刪除
Exit;
Dec(len, Count);// 移動到要刪除的位置
MoveMemory(PChar(P)+index*elSize , PChar(P)+(index + Count)*elSize , (len-index)*elSize); //移動內存
Dec(P); //移出 “數組長度”位置
Dec(P); //移出“引用計數” 位置
//重新再分配調整內存,len 新的長度. Sizeof(Longint) * 2 = 2*Dec(P)
ReallocMem(P, len * elSize + Sizeof(Longint) * 2);
Inc(P); // 指向數組長度
P^ := len; // new length
Inc(P); // 指向數組元素,開始的位置
PLongint(A) := P;
end;
對上面的例子,我們需要注意的是 elSize 參數 ,它必須是 SizeOf(DyArray_Name),表示元素所佔用的字節數。
相信看了上面的例子後,對於動態數組的拷貝,移動想必也可以自己實現了吧 J
後續:
其實,Delphi 對許多類型的內存分配都很相似,比如 string 類型,其實它和動態數組是很相似的,我們完全可以把它拿來當成動態數組。實質上 string 是 Pchar 的簡易版本。不管怎麼說,瞭解一些內存的分配對我們這些開發人員來說還是有一些好處的。