Delphi之动态数组.docx

上传人:b****2 文档编号:2555458 上传时间:2023-05-04 格式:DOCX 页数:12 大小:23.44KB
下载 相关 举报
Delphi之动态数组.docx_第1页
第1页 / 共12页
Delphi之动态数组.docx_第2页
第2页 / 共12页
Delphi之动态数组.docx_第3页
第3页 / 共12页
Delphi之动态数组.docx_第4页
第4页 / 共12页
Delphi之动态数组.docx_第5页
第5页 / 共12页
Delphi之动态数组.docx_第6页
第6页 / 共12页
Delphi之动态数组.docx_第7页
第7页 / 共12页
Delphi之动态数组.docx_第8页
第8页 / 共12页
Delphi之动态数组.docx_第9页
第9页 / 共12页
Delphi之动态数组.docx_第10页
第10页 / 共12页
Delphi之动态数组.docx_第11页
第11页 / 共12页
Delphi之动态数组.docx_第12页
第12页 / 共12页
亲,该文档总共12页,全部预览完了,如果喜欢就下载吧!
下载资源
资源描述

Delphi之动态数组.docx

《Delphi之动态数组.docx》由会员分享,可在线阅读,更多相关《Delphi之动态数组.docx(12页珍藏版)》请在冰点文库上搜索。

Delphi之动态数组.docx

Delphi之动态数组

动态数组之使用指针

 传统的Pascal语言其数组大小是预先确定的,当你用数组结构声明数据类型时,你必须指定数组元素的个数。

专业程序员也许知道些许动态数组的实现技术,一般是采用指针,用手工分配并释放所需的内存。

Delphi4中增加了非常简单的动态数组实现方法,实现过程效仿我前面讲过的动态长字符串。

与长字符串一样,动态数组的内存动态分配并且引用记数,不过动态数组不支持copy-on-write技术。

这不是个大问题,因为你可以把变量值设置为nil释放数组内存。

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

以下摘录的代码突出了一点,这就是:

定义数组后必须先为它分配内存,然后才能开始使用:

procedureTForm1.Button1Click(Sender:

TObject);varArray1:

arrayofInteger;beginArray1[1]:

=100;//errorSetLength(Array1,100);Array1[99]:

=100;//OK...end;

如果你只定义一个数组元素个数,那么索引总是从0开始。

Pascal中的普通数组既能用不为零的下标,也能用非整数的下标,但动态数组均不支持这两种下标。

象普通数组一样,你可以通过Length、High和Low函数了解到动态数组的状况,不过对于动态数组,Low函数返回值总是0,High函数返回数组大小减1,这意味着空的动态数组其函数High返回值是-1,这是一个很怪的值,因为它比Low的返回值还小。

图8.1:

例DynArr窗体

以上作了简短的介绍,现在举个简例,例名DynArr,见图8.1。

例子实在是很简单,其实动态数组没有什么特别复杂地方。

我想通过该例说明几个程序员可能犯的错误。

程序中声明了两个全程数组并在OnCreate事件中初始化了第一个数组:

varArray1,Array2:

arrayofInteger;procedureTForm1.FormCreate(Sender:

TObject);begin//allocateSetLength(Array1,100);end;

这样就把数组所有值设置为0。

完成这段代码你马上就能读写数组元素的值,而不用害怕内存出错,当然条件是你没有试图访问超过数组上界的元素。

为了更好地初始化,程序中添加了一个按钮,执行数组元素赋值操作:

procedureTForm1.btnFillClick(Sender:

TObject);varI:

Integer;beginforI:

=Low(Array1)toHigh(Array1)doArray1[I]:

=I;end;

Grow按钮用于修改数组大小,但并不影响数组内容。

单击Grow按钮后,你可以用Getvalue按钮进行检验:

procedureTForm1.btnGrowClick(Sender:

TObject);begin//growkeepingexistingvaluesSetLength(Array1,200);end;procedureTForm1.btnGetClick(Sender:

TObject);begin//extractCaption:

=IntToStr(Array1[99]);end;

Alias按钮的OnClick事件代码稍复杂些,程序通过:

=算子把一个数组拷贝给另一个数组,从而有效地创建了一个别名(一个新变量,但引用内存中同一数组)。

从中可见,如果你改变了其中一个数组,那么另一个同样也会改变,因为它们指向同一个内存区:

procedureTForm1.btnAliasClick(Sender:

TObject);begin//aliasArray2:

=Array1;//changeone(bothchange)Array2[99]:

=1000;//showtheotherCaption:

=IntToStr(Array1[99]);

在btnAliasClick事件中增加了两部分操作内容。

第一部分是数组等同测试,不过并不是测试实际的数组元素,而是测试数组所引用的内存区,检测变量是不是内存中同一数组的两个别名:

procedureTForm1.btnAliasClick(Sender:

TObject);begin...ifArray1=Array2thenBeep;//truncatefirstarrayArray1:

=Copy(Array2,0,10);end;

btnAliasClick事件的第二部分内容是调用Copy函数。

该函数不仅把数据从一个数组移到另一个数组,而且用函数创建的新数组取代第一个数组,结果变量Array1所引用的是11个元素的数组,因此,按Getvalue和Setvalue按钮将产生一个内存错误,并且触发一个异常(除非你把范围检查range-checking选项关掉,这种情况下,错误仍在但屏幕上不会显示异常)。

虽然如此,Fill按钮仍能正常工作,因为需要修改的数组元素由数组当前的下标范围确定。

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

   从Delphi4起,开始了内建各种类型的动态数组支持。

但是,对我们来说动态数组支持似乎做的不够彻底,因为Delphi竟然连删除、插入、移动连续元素的函数都没有提供,让人使用起来总觉得不够爽!

J。

作为一名程序员,我们当然要有自己解决问题的能力,下面就让我们简单介绍一下Delphi下的动态数组。

在Delphi中,数组类型有静态数组(a:

array[0..1024]ofinteger)、动态数组(vara:

arrayofinteger)、指针数组(即指向静态数组的指针)和开放数组(仅用于参数传递)。

静态数组、指针数组有速度快的好处,动态数组有大小可变的优势,权衡之下就有了折衷的办法,那就是定义的动态数组在必要时转换为指针。

动态数组声明之后,只有下面几个函数可:

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

ProcedureSetLength(varS;NewLength:

integer);

2.取出连续元素,复制给另一个数组变量

FunctionCopy(s;Index,Count:

integer):

array;

3.取得数组大小及上下限

FunctionLength(s):

integer;

FunctionHigh(x):

integer;

FunctionLow(x):

integer;

值得注意的是,不加const或var修饰的动态数组会被作为形参传递,而动态数组用const修饰并不意味着你不能修改数组里的元素(不信你可以字自己在程序中试试。

还有一点是High函数调用了Length函数,所以我们在获取数组上限时最好直接用Length(s)函数。

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

偏移量                                    内容

-8                                 32-bit引用计数

-4                                 32-bit数组长度

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

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

如果我们想要清空一个动态数组只需要把“数组长度”和“引用计数”清空即可。

”引用上面的一句话就是:

“权衡之下就有了折衷的办法,那就是定义的动态数组在必要时转换为指针。

”下面是清空动态数组的函数:

procedureDynArraySetZero(varA);

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删除的数量

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

procedureDynArrayDelete(varA;elSize:

Longint;index,Count:

Integer);

var

 len,MaxDelete:

Integer;

 P:

PLongint;//4个字节的长整形指针

begin

 P:

=PLongint(A);//取的A的地址

 ifP=nilthen

   Exit;

 {

下面这句完全等同于Dec(P);len:

=P^ 因为Dec(P)=Pchar(P)–4 同样是移动4字节的偏移量,只不过后者按字节来移动   }

len:

=PLongint(PChar(P)-4)^;//变量的长度,偏移量-4

 ifindex>=lenthen//要删除的位置超出范围,退出

   Exit;

 MaxDelete:

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

 Count:

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

 ifCount=0then//不要求删除

   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;//newlength

 Inc(P);//指向数组元素,开始的位置

 PLongint(A):

=P;

end;

 

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

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

后续:

   其实,Delphi对许多类型的内存分配都很相似,比如string类型,其实它和动态数组是很相似的,我们完全可以把它拿来当成动态数组。

实质上string是Pchar的简易版本。

不管怎么说,了解一些内存的分配对我们这些开发人员来说还是有一些好处的。

动态数组之参数传递

初用Delphi中的动态数组时,有些迷惑.

1、动态数组是指针吗?

动态数组通常会表现出指针功能,先看下面的例子:

procedureDymArrTest();

var

 A,B:

arrayofInteger;

begin

 SetLength(A,3);

 A[0]:

=0;

 B:

=A;

 B[0]:

=1;

 ShowMessage(IntToStr(A[0]));

end;

在以上程序中,A、B数组指向的是相同的地址,所以改变了B[0],结果A[0]会等于1。

虽然动态数组暗含指针功能,但它的内存管理却是生存期管理类型,无需手工释放。

2、为什么改变形参却没有改变实参的值?

虽然动态数组会表现出指针的功能,在直接用它声明形参,改变形参值时,却是不会改变实参的,看下面的例子:

procedureDymArrParam(Arr:

arrayofInteger);

var

 I:

Integer;

begin

 forI:

=0toLength(Arr)-1do

   Arr[I]:

=2;

end;

procedureTForm1.Button1Click(Sender:

TObject);

var

 A:

 arrayofInteger;

 I:

Integer;

begin

 SetLength(A,2);

 forI:

=0toLength(A)do

   A[I]:

=1;

 DymArrParam(A);//试图把数组中的元素值改变为2

 Caption:

=IntToStr(A[0])+','+IntToStr(A[1]);

end;

可以看到最终并没有把实参数组A元素值变为2。

原因是这种传递方法编译器会在DymArrParam中复制了一个A的副本,而不是像在第1点中所期望的那样改变了A的值,可见动态数组并非就等同指针。

3、如何通过动态数组的形参改变实参值?

1>var

在Delphi中很显然方法的是,在DymArrParam过程中通过var修饰形参,即

procedureDymArrParam(varArr:

arrayofInteger);

在不想通过形参改变实参值时,可以用const修饰形参,明确表示出不可修改意思,即

procedureDymArrParam(constArr:

arrayofInteger);

这样做之后,如果对Arr赋值的话,将会编译不通过。

2>type

另一种就是用type重新定义一个类型,

例如:

type

 TDymIntArray=arrayofInteger;//在system单元已经声明了个TBoundArray,用它也可以

然后用于形参声明,如下:

procedureDymArrParam(Arr:

TDymIntArray);

调用此函数时用的实参的类型也必须为TDymIntArray。

这样做,不用var修饰形参,也一样可以通过形参改变实参。

在这种情况下,即使用const修饰形参,也一样可以修改形参的值来改变实参的值。

delphi动态数组的使用

动态数组的使用

//例1:

var

StrArr:

arrayofString;//动态数组定义时不与维数

begin

SetLength(StrArr,6);//分配6个元素位置:

0-5

StrArr[0]:

='万一';//动态数组的下界是0

ShowMessage(StrArr[0]);//分配空间后和静态数组一样使用

StrArr:

=nil;//一般没必要手动释放,动态数组离开作用域会自释放

end;

--------------------------------------------------------------------------------

//例2.动态数组的引用:

var

Arr1,Arr2:

arrayofInteger;

a:

array[0..1]ofInteger;

begin

SetLength(Arr1,6);

Arr1[5]:

=100;

Arr2:

=Arr1;//Arr2引用了Arr1

ShowMessage(IntToStr(Arr2[5]));//100

ShowMessage(IntToStr(Length(Arr2)));//当然Arr2维数也会是6

ShowMessage(IntToStr(SizeOf(Arr1)));//4,其实动态数组是个指针

ShowMessage(IntToStr(SizeOf(Arr2)));//4

Arr2[5]:

=99;//现在它们指向同一个数组,改变这个就是改变那个

ShowMessage(IntToStr(Arr1[5]));//99

Arr1:

=nil;//释放其中一个指针,数组继续存在

ShowMessage(IntToStr(Arr2[5]));//99

end;

--------------------------------------------------------------------------------

//例3.数组Copy<1>:

var

Arr1,Arr2:

arrayofInteger;

begin

SetLength(Arr1,6);

Arr1[5]:

=100;

Arr2:

=Copy(Arr1);//数组Copy

Arr2[5]:

=99;//改变Arr2不再影响Arr1

ShowMessage(IntToStr(Arr1[5]-Arr2[5]));//1

SetLength(Arr1,7);

ShowMessage(IntToStr(Length(Arr1)));//7

ShowMessage(IntToStr(Length(Arr2)));//6,没有一点牵扯了

end;

--------------------------------------------------------------------------------

//例4.数组Copy<2>:

var

Arr1,Arr2:

arrayofInteger;

i:

Integer;

begin

SetLength(Arr1,6);

fori:

=Low(Arr1)toHigh(Arr1)do//给每个元素赋值

Arr1[i]:

=i+1;

Arr2:

=Copy(Arr1,1,3);//只Copy第2..4个元素

ShowMessage(IntToStr(Arr1[1]));//2,现在Arr2[0]和Arr1[1]的值是一样的

ShowMessage(IntToStr(Arr2[0]));//2

ShowMessage(IntToStr(Length(Arr1)));//6,维数肯定不一样了

ShowMessage(IntToStr(Length(Arr2)));//3

end;

--------------------------------------------------------------------------------

//例5.动态多维数组:

var

Arr:

arrayofarrayofInteger;//定义多维数组

begin

SetLength(Arr,5,5);//分配空间

Arr[0,3]:

=100;//赋值

ShowMessage(IntToStr(Arr[0,3]));//取值

end;

--------------------------------------------------------------------------------

//例6.另类建立:

var

Arr:

arrayofInteger;

begin

Arr:

=varArrayCreate([0,3],varInteger);

ShowMessage(IntToStr(Length(Arr)));//4

Arr:

=VarArrayOf([1,2,3,4]);

ShowMessage(IntToStr(Arr[0]));//1

{这是给变体数组使用的,可能会有效率问题}

end;

数组的表达:

var

A:

arrayofarrayofstring;

I,J:

Integer;

begin

SetLength(A,10);

forI:

=Low(A)toHigh(A)do

begin

SetLength(A[I],I);

forJ:

=Low(A[I])toHigh(A[I])do

A[I,J]:

=IntToStr(I)+','+IntToStr(J)+'';

end;

end;

{赋值}

fori:

=0toic-1do

forj:

=0tojc-1do

fork:

=0tokc-1do

//Arr[i][j][k]:

=Random(100);

Arr[I,J,K]:

=RandomRange(100,10000);//两句表达都可以。

展开阅读全文
相关资源
猜你喜欢
相关搜索
资源标签

当前位置:首页 > 解决方案 > 学习计划

copyright@ 2008-2023 冰点文库 网站版权所有

经营许可证编号:鄂ICP备19020893号-2