Delphi - 数组 详解

技术交流,DH讲解.

首先我们要知道什么是数组?数组是一堆相同特性数据的一个集合,也就是每个元素的类型必须是一样的,当然在其他一些弱语法的语言里面,数组的元素可以千奇百怪.

例子:

?

1

2

3

4

5

6

Var

A:Array[0..2]OfInteger;

Begin

A[0] :=1;

A[1] :=1.0;//这里是错的,因为每个元素都必须是Integer类型

End;

Delphi中数组分类:

1 定长和不定长.

定长数组:也就是长度在声明的时候就确定了,后面是不能改变的,而在定长数组中,起始序号不必从0开始,可以自己定.例如:

?

1

2

3

4

5

6

Var

A:Array[2..3]OfInteger;

Begin

A[2] :=1;

SetLength(A,3);//这里会出错,定长数组不能再分配

End;

从上面我们可以看到起始序号是2,但是步长是1,是不能改变的.为什么我们看到很多数组的起始序号都是0呢?习惯而已.大家都习惯在厕所里面去嘘嘘,而你一个人习惯在广场上面嘘嘘,那么大家都会说你不文明了.但是如果大家一开始都是在广场上面嘘嘘的话,不说了太恶心了.

来看一个特殊的用法:

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

type

THuangJacky = (hjA,hjB,hjC);

const

//用法1

B:array[0..2]ofstring= ('A','B','C');

//用法2

C:array[THuangJacky]ofstring= ('A','B','C');

Var

H:THuangJacky;

S:string;

Begin

S:=B[Ord(H)];

S:=C[H];

//B[H] 和 C[1]都会出错

End;

用法1 和用法2你觉得那种用着爽一些?

从上面例子可以看出来只要是序数类型都可以当数组的序号.但是我们用的时候序号就必须是声明的那种序数类型,所以上面代码注释中才会写出2种错误的情况.

不定长数组:动态数组,也就是声明的时候没有说长度是多少,在使用前必须声明,长度可以再分配.序号必须从0开始.看个简单的例子

?

1

2

3

4

5

6

7

8

9

10

11

12

13

Var

A:ArrayOfInteger;

Begin

SetLength( A,3) ;//数组一共有3个元素

A[0] :=1;

A[1] :=2;

A[2] :=3;

//A[3]没有它,有它的话,你数一下几个了?不会数数,那我推荐你去街道口小学看一下

SetLength( A,4) ;//如果变长长度,直接增加后面的元素

A[3] :=4;//现在有它了.

SetLength( A,3) ;//如果长度变短,超出部分会被去掉

// A[3]又没有它了

End;

有时候大家这样要先设定长度,然后再赋值,是不是很麻烦?没有一气呵成的感觉.好吧,再说一招:

?

1

2

3

4

5

6

7

8

9

Type

TA =ArrayOfInteger;

Var

A: TA ;

Begin

A := TA.Create(1,2,3) ;

//此招请勿在D7上面使用

//这样A[0]:=1,A[1]:=2,A[2]:=3

End;

2 一维和多维.

前面所有例子,我们都只是说了一维数组,要是现在我们想弄一个矩阵(多维数组)怎么办?

?

1

2

3

4

5

6

7

Var

A:Array[0..2,0..2]OfInteger;

B:Array[0..2]OfArray[0..2]OfInteger;

Begin

A[0,0]:=1;

A[0][0]:=1;

End;

两种方法都可以的.

?

1

2

3

4

5

6

7

8

9

10

Var

B:ArrayOfArrayOfInteger;

Begin

SetLength(B,3,3);// 3*3矩阵

// 如果要实现齿状数组,必须这么做

SetLength(B,3);

SetLength(B[0],1);// *

SetLength(B[1],2);// **

SetLength(B[2],3);// ***

End;

接下来我们说说几个关于数组中常用的函数:

第一个 复制数组

?

1

2

3

4

5

6

7

8

9

Var

A, B:Array[0..1]OfInteger;

Begin

A[0]:=1;

A[1]:=2;

B:= A;

B[0]:=2;

ShowMessageFmt('A0:%D,B0:%D', [A[0], B[0]]);// A0:1,B0:2

End;

这个效果就是我们想要的,貌似没有什么好说的.如果是动态数组呢?

?

1

2

3

4

5

6

7

8

9

10

11

Var

A, B:ArrayOfInteger;

Begin

SetLength(A,2);

SetLength(B,2);

A[0]:=1;

A[1]:=2;

B:= A;

B[0]:=2;

ShowMessageFmt('A0:%D,B0:%D', [A[0], B[0]]);// A0:2,B0:2

End;

现在怎么办?A和B被关联到了一个地址了,其实现在我们可以使用Copy函数,对就是复制字符串的那个函数:

?

1

2

3

4

5

6

7

8

9

10

11

12

Var

A, B:ArrayOfInteger;

Begin

SetLength(A,2);

SetLength(B,2);

A[0]:=1;

A[1]:=2;

B:= Copy(A);// 整个数组都复制过去

B:= Copy(A,0,2);// 选择性复制

B[0]:=2;

ShowMessageFmt('A0:%D,B0:%D', [A[0], B[0]]);// A0:1,B0:2

End;

第二个 序号相关

函数Low()和High()值得信赖,不过我们需要注意的是,它们返回的类型是我们数组的序号的那个类型,并不都是Integer,如前面例子中的THuangJacky.

?

1

2

3

4

5

6

7

8

9

10

11

12

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;

第三个 数组长度

Length()函数返回的就一定是是Integer了,因为个数不是整数难道还有半个么?

?

1

2

3

4

5

6

Var

A:ArrayOfInteger;

Begin

SetLength(A,2);

Length(A);// 返回2

End;

?

1

最后说个问题我就不说了:

从上面那个复制的例子我们可以看出来什么东西?

定长数组变量就是一个变量,所以可以直接用:=来赋值,而动态数组变量就是一个指针,如果用了:=来赋值2个变量就关联在一起了.

?

1

2

3

4

5

6

7

8

Var

A:Array[0..2]OfInteger;

B:ArrayOfInteger;

Begin

ShowMessageFmt('A:%8x,A[0]:%8p', [Integer(@A), @A[0]]);// 一样,从地址来看这个数组空间在栈上面

SetLength(B,3);

ShowMessageFmt('B:%8p,B[0]:%8p', [B, @B[0]]);// 一样,这个数据空间在堆上面

End;

我们看到A要取地址才和A[0]取地址一样,那么也就是说A就是A[0].

而B直接就和B[0]取地址一样了,也就是说B就是B[0]的地址.

数组在内存中的分布:连续分布的,间隔就是每个元素的大小.

?

1

2

3

4

5

6

7

8

9

10

11

12

13

Var

A:Array[0..2]OfInteger;

B:ArrayOfInteger;

Begin

A[1]:=123;

// 从A也就是A[0]的地址上面 往下走4个直接 就是A[1]

ShowMessageFmt('A[1]:%D,直接取值:%D', [A[1], PInteger(Integer(@A)+4)^]);

// 一样,都是123

SetLength(B,3);

B[2]:=88;

// 从B往下走8个字节就是B[2]

ShowMessageFmt('B[2]:%D,直接取值:%D', [B[2], PInteger(Integer(B)+8)^]);// 一样,88

End;

但是动态数组的结构和字符的结构就很像了:

偏移-8-40~Length*元素大小-1
内容32位引用次数元素个数实际内容

SetLength: 设定数组长度

SetLength(MyArray, 5) // 设定MyArray数组长度为5

High: 传回注脚的最大值,如果参数为数组类型的话,则是返回数组索引的最大值

High(MyArray) // 返回值为5

Copy: 字符串截取函数

Copy('substring', 4, 3) // 返回值为‘str’