专注于互联网--专注于架构

最新标签
网站地图
文章索引
Rss订阅

首页 »Delphi教程 » delphi动态数组:Delphi之动态数组使用总结 »正文

delphi动态数组:Delphi之动态数组使用总结

来源: 发布时间:星期四, 2009年2月12日 浏览:249次 评论:0


传统Pascal 语言其大小是预先确定当你用结构声明数据类型时你必须指定元素个数专业员也许知道些许动态实现技术般是采用指针用手工分配并释放所需内存 Delphi 4中增加了非常简单动态实现思路方法实现过程效仿我前面讲过动态长和长动态内存动态分配并且引用记数不过动态不支持 copy-on-write 技术这不是个大问题你可以把变量值设置为nil释放内存

这样你就可以声明个不指定元素个数并用SetLength 过程给分配个特定大小内存SetLength 过程还可以改变大小而不影响其内容除此外还有串过程也可用于如Copy

以下摘录代码突出了这就是:定义后必须先为它分配内存然后才能开始使用:

procedure TForm1.Button1Click(Sender: TObject);var Array1: .gif' /> of Integer;begin Array1 [1] := 100; // error SetLength (Array1, 100); Array1 [99] := 100; // OK ...end;

如果你只定义元素个数那么索引总是从0开始Pascal 中普通既能用不为零下标也能用非整数下标但动态均不支持这两种下标象普通你可以通过Length、High和Low 了解到动态状况不过对于动态Low 返回值总是0High返回大小减1这意味着空动态High返回值是-1这是个很怪它比Low返回值还小

图 8.1: 例 DynArr 窗体

\"\"\" width=203 border=0>

以上作了简短介绍现在举个简例例名DynArr 见图8.1例子实在是很简单其实动态没有什么特别复杂地方我想通过该例介绍说明几个员可能犯中声明了两个全程并在OnCreate 事件中化了第:

var Array1, Array2: .gif' /> of Integer;procedure TForm1.FormCreate(Sender: TObject);begin // allocate SetLength (Array1, 100);end;

这样就把所有值设置为0完成这段代码你马上就能读写元素而不用害怕内存出错当然条件是你没有试图访问超过上界元素为了更好地中添加了个按钮执行元素赋值操作:

procedure TForm1.btnFillClick(Sender: TObject);var I: Integer;begin for I := Low (Array1) to High (Array1) do Array1 [I] := I;end;

Grow 按钮用于修改大小但并不影响内容单击Grow 按钮后你可以用Get value按钮进行检验:

procedure TForm1.btnGrowClick(Sender: TObject);begin // grow keeping existing values SetLength (Array1, 200);end;procedure TForm1.btnGetClick(Sender: TObject);begin // extract Caption := IntToStr (Array1 [99]);end;

Alias 按钮OnClick 事件代码稍复杂些通过 := 算子把拷贝给另从而有效地创建了个别名(个新变量但引用内存中同)从中可见如果你改变了其中那么另个同样也会改变它们指向同个内存区:

procedure TForm1.btnAliasClick(Sender: TObject);[Page]begin // alias Array2 := Array1; // change _disibledevent=> // show the other Caption := IntToStr (Array1 [99]);

btnAliasClick 事件中增加了两部分操作内容部分是等同测试不过并不是测试实际元素而是测试所引用内存区检测变量是不是内存中同两个别名:

procedure TForm1.btnAliasClick(Sender: TObject);begin ... Array1 = Array2 then Beep; // truncate first .gif' /> Array1 := Copy (Array2, 0, 10);end;

btnAliasClick 事件第 2部分内容是Copy 不仅把数据从移到另而且用创建取代第结果变量Array1 所引用是11个元素因此按Get value 和Set value 按钮将产生个内存并且触发个异常(除非你把范围检查range-checking 选项关掉这种情况下仍在但屏幕上不会显示异常)虽然如此Fill 按钮仍能正常工作需要修改元素由当前下标范围确定

自从有了动态链表除了在教科书里出现外已经很少在实际编程中被使用了事实也是如此确比传统链表快得多,而且也方便



从 Delphi4起开始了内建各种类型动态支持但是对我们来说动态支持似乎做不够彻底Delphi竟然连删除、插入、移动连续元素都没有提供让人使用起来总觉得不够爽!!! J 作为我们当然要有自己解决问题能力下面就让我们简单介绍下Delphi 下动态

在Delphi中类型有静态(a : .gif' />[0..1024] of eger)、动态(var a : .gif' /> of eger)、指针(即指向静态指针)和开放(仅用于参数传递)静态、指针有速度快好处动态有大小可变优势权衡的下就有了折衷办法那就是定义动态在必要时转换为指针

动态声明的后只有下面几个可┎僮鳎?o:p>

1. 设置大小可以任意缩减或增加大小

Procedure SetLength(var S ; NewLength : eger);

2. 取出连续元素复制给另变量

Function Copy(s;Index,Count : eger) : .gif' /> ;

3. 取得大小及上下限

Function Length(s):eger;

Function High(x):eger;

Function Low(x):eger;

值得注意不加const或var修饰动态会被作为形参传递而动态用const修饰并不意味着你不能修改元素(不信你可以字自己在中试试还有点是High了Length 所以我们在获取上限时最好直接用 Length(s)

动态在内存空间中占用4个字节. 动态在内存中分配表如下:

偏移量 内容[Page]

-8 32-bit 引用计数

-4 32-bit 长度

0..长度 * (元素尺寸) - 1 元素 元素尺寸=Sizeof(元素类型)

根据上面分配情况可以得到如下结果:

如果我们想要清空个动态只需要把“长度”和“引用计数”清空即可”引用上面句话就是:“权衡的下就有了折衷办法那就是定义动态在必要时转换为指针”下面是清空动态:

procedure DynArraySetZero(var A);

var



P: PLong; //占用4个字节正好符合 32 位内存排列

begin

P := PLong(A); // 指向 A 地址

Dec(P); //P 地址偏移量是 (A)指向了长度

P^ := 0; // 长度清空

Dec(P); // 指向引用计数

P^ := 0; //计数清空

end;

上面就这么简单而且效率也非常高

下面让我们再来看看怎样删除动态元素,体如下:

{************************************

A 变量类型 elSize = SizeOf(A)

index 开始删除位置索引 Count 删除数量

****************************************}

procedure DynArrayDelete(var A; elSize: Long; index, Count: Integer);

var

len, MaxDelete: Integer;

P : PLong; //4 个字节长整形指针

begin

P := PLong(A);// 取 A 地址

P = nil then

Exit;

{

下面这句完全等同于 Dec(P) ; len := P^ Dec(P) = Pchar(P) – 4 同样是移动4 字节偏移量只不过后者按字节来移动 }

len := PLong(PChar(P) - 4)^; // 变量长度 偏移量 -4

index >= len then //要删除位置超出范围退出

Exit;



MaxDelete := len - index; // 最多删除数量

Count := Min(Count, MaxDelete); // 取得个较小值

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(Long) * 2 = 2*Dec(P)

ReallocMem(P, len * elSize + Sizeof(Long) * 2);

Inc(P); // 指向长度

P^ := len; // length [Page]

Inc(P); // 指向元素开始位置

PLong(A) := P;

end;



对上面例子我们需要注意是 elSize 参数 它必须是 SizeOf(DyArray_Name),表示元素所占用字节数

相信看了上面例子后对于动态拷贝移动想必也可以自己实现了吧 J

后续:

其实,Delphi 对许多类型内存分配都很相似比如 类型其实它和动态是很相似我们完全可以把它拿来当成动态实质上 是 Pchar 简易版本不管如何说了解些内存分配对我们这些开发人员来说还是有些好处

0

相关文章

读者评论

发表评论

  • 昵称:
  • 内容: