Delphi中的动态数组总结
今天做的⼀个项⽬中要使⽤⼀⼤串数据进⾏处理。如何对这⼀系列的数据进⾏保存成为⼀个⾸要的问题。唉,Delphi啊,你何时才能⽀持泛型啊。在C#,C++这都不是问题了啊。在Delphi⾥只有TStringList这个容器可以使⽤,但是它是处理字符串的。⼀进⼀出,需要⼤量的typecast时间。⽤链表??不值得。上⽹⼀查,呵呵,可以⽤动态数组。看来我还是⼀个新⼿,这个东西我都不知道啊。啥时候Delphi有了这个玩意了。
var
i:array of integer;
begin
SetLength(i,length);//设置动态数组的长度
以后就可以像正常数组那样操作
有了动态数组就可以随时设置数组的⼤⼩,不会像以前那样事先定义数组的⼤⼩,对资源造成浪费。
动态数组的本质还是指针,上⾯的例⼦。i 就是⼀个指针类型。可以将两个动态数组之间进⾏引⽤赋值,当然了我们也可以将其复制给⼀个Pointer,只不过意义不⼤罢了。
var
p:Pointer;
i,j:Array of integer;
begin
SetLength(i,20);//分配内存空间
i[0] := 22;
i := j;//此时,i,j都同时指向了那段动态数组的存
p := i; //没有实际意义,只是证明了i 是指针类型
//释放⼯作
end;
使⽤完了内存当然需要释放了,否则会造成内存泄露。动态数组使⽤了 reference-counting 技术,所以在使⽤完后,只需将其赋值为nil即可。
-------------------------------------------------------------------
⾃从有了动态数组,链表除了在教科书⾥出现外,已经很少在实际编程中被使⽤了,事实也是如此,数组的确⽐传统链表快得多,⽽且也⽅便的多。
  从 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 的简易版本。不管怎么说,了解⼀些内存的分配对我们这些开发⼈员来说还是有⼀些好处的。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。