本章要点:
数组的基本概念
静态数组的定义和使用
动态数组的定义和使用
数组的应用第 4章 数 组
4.1 静态数组数组是相同类型的元素按一定顺序组成的序列。数组中的每一个元素都可以通过数组名和惟一一个索引号来存取。在 Object Pascal中可以定义数组元素个数一定的数组,即静态数组。根据静态数组的维数可以把数组分为一维静态数组、二维静态数组和多维静态数组。
静态数组在程序初始化时必须分配内存单元,明确其固定的大小和元素的数据类型。
4.1.1 定义
1,一维静态数组一维静态数组类型声明格式为:
Type
<数组类型名称 >=array[<下标 >]of <基类型 >;
如定义一个数组类型:
Type
MyArray=array[1..100]of Real;
表示定义了一个有 100个元素的数组类型,元素下标从 1到 100,
每个数组元素都是 Real类型,数组类型标识符为 MyArray。
4.1.1 定义 (1)
顺序控制结构是计算机按照语句的前后顺序依次执行的程序结构。在顺序控制结构中,程序依次地经过输入、处理到最后的输出。 Delphi 7.0提供了基本的输入 /输出控件(在第 7章中将给予介绍)。对于顺序控制结构来说,
组成程序体主要语句就是赋值语句。接下来通过一个实例来说明顺序控制结构的程序设计方法。
【 例 3-1】 大小写转换程序。
1,主要步骤
进入 Delphi 7.0后在 standard标签页中找到相关组件,设计如图 3-1窗体及有关对象。
分别在窗体的建立、小写按钮单击和大写按钮单击事件中输入如下代码。
图 3-1 大小写转换程序
4.1.1 定义 (2)
数组类型标识符可以是任何合法的标识符。 Object Pascal 允许下标的类型为整数类型、字符类型、布尔类型、子界类型、枚举类型等。而元素类型可以为任意的数据类型,但在同一数组中,所有元素的数据类型必须相同。
对于用户定义的数据类型作为下标类型,在使用之前必须声明。例如可以进行如下定义:
procedure TForm1.FormCreate(Sender,TObject);
begin
edit1.Text:=''; //初始化文本框,使其为空
end;
procedure TForm1.Button1Click(Sender,TObject);
var s:string;
begin
s:=edit1.Text;
edit1.Text:=lowercase(s); // 转换成小写
end;
4.1.1 定义 (3)
Type
Color=( red,green,blue) ;
Number=0..100;
MyArray=Array[Color] of Integer;
ColorArray=Array[Number] of Color;
以上就定义了一个以 MyArray为标识的数组类型,其元素下标分别为 red、
green和 blue,每个数组元素都是 Integer类型。
经过数组类型声明之后才可以定义数组变量。就像定义整型或实型变量一样,
通过变量说明语句定义变量,例如:
var Array1,Array2:MyArray;
上面定义了两个数组变量 Array1和 Array2,它们的数据类型是 MyArray;
也可以把数组类型的定义和数组变量的定义组合起来,以便简化代码,例如:
var Array1,Array2:Array[1..100] of real;
要访问数组中的元素,可以用数组名加方括号,方括号内是元素的下标值,
如 Array1[3],Array2[20]等。方括号内的下标值必须符合数组类型的定义,
其类型必须与下标类型一致,其值在下标取值范围。另外,下标可以是表达式。
4.1.1 定义 (5)
2,High()和 Low()函数
Object Pascal的数组和其他语言的数组有一点不同,即数组不必以特定的数为基准。例如可以从 15开始声明一个具有 3个元素的数组类型:
Var A:Array[15..17] of Integer;
Object Pascal的数组不一定从序号 0或 1开始的,因此在循环计算中需要小心处理数组边界。系统提供了内建的 High()和 Low()函数,这两个函数分别返回数组变量或数组类型的上限值或下限值。建议使用 Low()和 High()操作数组,特别是在循环中,因为这样能使代码与数组范围无关,如果你改变数组下标的范围声明,Low()和 High()代码不会受影响。否则,如果代码中有一个数组下标循环体,那么当数组大小改变时你就不得不更新循环体的代码。因此在循环中用这两个函数做边界控制会使程序易于维护且不容易出错。
注意,使用标准函数 Low()和 High()不会增加系统运行额外开销。因为在编译时,它们已被转换成常数表达式,而不是实际函数调用,而函数 Length()可以返回数组的长度。
4.1.1 定义 (6)
3,二维及多维静态数组二维数组是指一个一维数组中的元素类型本身又是一个一维数组,声明二维数组的一般形式为:
Type
<数组类型名称 >=Array[<下标类型 1>,<下标类型 2>]of <基类型 >;
例如:
Type
RealArray=Array[1..20,1..100]of Real;
以把二维数组看做是一个矩阵,其中下标 1是行,下标 2是列,这样要见如下代码:
var
A:array[15..17] of Integer;
i:Integer;
begin
for i:=Low(A) to High(A) do
A[i]:=i;
end;
4.1.1 定义 (7)
多维静态数组的一般格式为:
Type
<数组类型名称 >=Array[<下标类型 1>,<下标类型 2>,…,< 下标类型 n>]of <基类型 >
一般情况,用户的数组不超过三维数组,用到三维以上的很少,但
Object Pascal允许定义任意维数的数组。
静态数组通过下标类型、维数明确了数组的大小。动态数组使用时没有说明数组的大小,只是在程序设计中为程序动态地开辟存储空间。
访问二维数组的元素可以写成 array1[2,3],即访问第 2行第 3列的元素。对于多维数组可以使用循环语句给数组赋值,例如:
var
Col,Row,Integer;

for Col,=1 to 20 do
for Row,=1 to 100 do
RealArray[Col,Row]:=0;

4.1.2 应用 (1)
接下来利用数组来解决排序的问题。
【 例 4-1】 对 20个数进行排序(由大到小)。
1,主要步骤
进入 Delphi7.0后,按图 4-1设计窗体及有关对象。
定义一个全局数组变量,语句为:
var A:Array[1..20]of Integer;
图 4-1 静态数组分别在两个按钮的单击和窗体的建立事件中输入如下代码:
procedure TForm1.Button1Click(Sender,TObject); //产生随机数
Var i:Integer;
s,t:String;
begin
Randomize;
s:='';
for i:=Low(A) to High(A) do
begin
4.1.2 应用 (2)
procedure TForm1.Button2Click(Sender,TObject); //排序
var i,j,temp:integer;
s,t:String;
begin
for i:=Low(A) to High(A) do
for j:=i+1 to High(A) do
if (A[i]<A[j]) then
A[i]:=random(50); //产生一个大于等于 0小于 50的随机数
str(A[i],t); //以字符类型赋给 t
s:=s+t+' ';
//s对 t进行累加并且加空格
end;
label1.Caption:=s; //显示
button2.Enabled:=true;//允许进行排序
end;
4.1.2 应用 (3)
begin
temp:=A[j];
A[j]:=A[i];
A[i]:=temp;
end;
for i:=Low(A) to High(A) do
begin
str(A[i],t);
s:=s+t+' ';
end;
label2.Caption:=s;
button2.Enabled:=false; //禁止进行排序
end;
procedure TForm1.FormCreate(Sender,TObject); //窗体创建
begin
button2.Enabled:=false;
end;
4.1.2 应用 (4)
2,分析首先,定义一个全局数组变量的目的是可以在程序的两个按钮事件中都能被访问到。在产生随机数按钮中(即 button1按钮)使用了一个 for循环,其中 A[i]:=random(50)语句是将产生的随机数赋值给数组,50表示产生的随机整数在 0和 50之间,并且将数组中各元素值通过 label1标签进行输出。在排序按钮中(即 button2按钮)先是使用了双重 for循环,目的是对数组进行排序。其主要思想是将相邻的两个数 A[1],A[2]比较,如果前者小于后者则交换,再将 A[2]与 A[3]进行比较并进行相同处理,直到将最后两个数比较并处理完毕。这时最大的一个数已换到了最前一个位置,
这是第一轮的比较和处理。然后进行下一轮比较,把剩下的数中最大的数移到最前的第二个位置。进行 High(A)轮后就可将数组中的元素从大到小的顺序排列了。第三个 for循环是将重新排列的数组元素转换成字符串,
最后通过 label2标签进行输出。
3,运行结果运行以上程序,结果如图 4-2所示。 图 4-2 运行结果
4.2 动态数组静态数组在程序初始化时必须明确其固定的大小和类型 。 然而在有些时候,希望在数组使用的时候再分配内存,这种数组称为动态数组 。
动态数组是指在定义数组时不说明数组元素个数而是在程序运行时确定。
1,一维动态数组一维动态数组类型声明格式为:
Type
<数组类型名称 >=array of <基类型 >;
也可在声明变量时直接声明一维动态数组:
var
<变量名 >,array of <基类型 >;
如下代码:
var Myarray:array of integer;
就声明了一个变量名为 Myarray的元素类型为一维整型的动态数组。
4.2.1 定义
4.2.1 定义 (1)
一维动态数组的声明中没有给出数组的下标类型,因此就可以声明一个不指定元素个数的数组 。 在程序设计中,通过调用标准过程 Setlength
来明确动态数组的大小 。
var
Myarray,array of real; //定义一个名为 Myarray的动态数组
begin
Myarray[1]:=1.52; //错误,没有明确数组大小前是不能对数组赋值的
setlength(Myarray,30); //设置动态数组长度为 30,下标从 0到 29
Myarray[2]:=4.56; //正确
end;需要注意的是,Object Pascal 中的普通数组既能用不为零的下标,也能用非整数的下标,但一维动态数组均不支持这两种下标。和普通数组一样,
可以通过 Length(),High()和 Low()函数了解到动态数组的状况,不过对于一维动态数组,Low()函数返回值总是 0,High()函数返回数组大小减 1。
注意,动态数组总是从零开始的。在一个空的动态数组中,Low 函数返回值是 0,High函数返回值是 -1,比它 Low的返回值还小。
4.2.1 定义 (2)
动态数组是生存期管理类型,因此用完之后可以不需要手工释放内存,
delphi 7.0会在动态数组超出作用域后释放动态数组。然而,在有些时候(例如在动态数组占用了大量内存的场合)可能需要在动态数组未超出其作用域时提前释放它们。这种情况下只需把动态数组赋值为 nil即可,如下代码:
Myarray:=nil; //释放 Myarray数组动态数组在引用操作上和静态数组也有不同之处,来看下面的例子。
Var DA,DB,array of integer;
SA,SB:array [1..10] of integer;
begin
setlength(DA,10);
DA[1]:=1;
SA[1]:=1;
DB:=DA; // 将 DB数组指向同一个数组 DA
SB:=SA; // 从 SA数组中复制一个新的数组给 SB
DB[1]:=0;
SB[1]:=0;
end;
4.2.1 定义 (3)
2,多维动态数组动态数组也可以是多维的,要声明多维数组,可以采用递归的方式来定义。
可以在一维数组的 array of后面再附加上 array of,
Type
<数组类型名称 >=array of array of … array of < 基类型 >;
var
<变量名 >,<数组类型名称 >;
当然,也可以在声明变量时直接声明多维动态数组,如下定义:
var
<变量名 >,array of array of … array of < 基类型 >;
上述代码中声明了 DA,DB两个一维动态数组和 SA,SB两个一维静态数组,
经过一系列赋值后,DA[1],SA[1]的值应该是多少呢?是不是还是 1呢?
实际上,对于静态数组而言,SB:=SA语句将产生一个新的数组 SB,它的内容和 SA数组完全一样。而对于动态数组而言,DB:=DA语句并没有产生一个新的数组,而是将 SB数组指向 DA数组。因此,执行上述代码后,SA[1]的值没有发生变化,还是 1;而 DA[1]的值就发生了变化,变成了 0。
4.2.1 定义 (4)
如下代码定义了一个变量名为 A的元素数据类型为实型的二维动态数组:
var
A:array of array of real;
相应地,如果要设定一个 4× 5的二维动态数组可将 setlength
函数写成:
setlength(A,4,5);
4.2.2 应用 (1)
接下来介绍利用动态数组进行矩阵的运算。
【 例 4-2】 矩阵运算。
1,主要步骤
进入 Delphi 7.0后,按图 4-3设计窗体及有关对象。
定义两个全局二维动态数组变量,语句为:
var a:array of array of integer;
b:array of array of integer;
分别在初始化和计算按钮的单击事件和窗体建立事件中输入如下代码:
图 4-3 矩阵运算
procedure TForm1.Button1Click(Sender,TObject); //初始化按钮单击事件
var i,j:integer;
s:string;
begin
4.2.2 应用 (2)
randomize;
for i:=0 to high(a) do
begin
for j:=0 to high(a[0])
do
begin
a[i,j]:=random(10);
//产生随机数赋给 a 数组
s:=s+inttostr(a[i,j])+#32;
end;
s:=s+#13;
end;
label1.Caption,=s;
s:='';
for i:=0 to high(b) do
begin
for j:=0 to high(b[0]) do
begin
b[i,j]:=random(10);
//产生随机数赋给 b 数组
s:=s+inttostr(b[i,j])+#32;
end;
s:=s+#13;
end;
label2.Caption,=s;
end;
procedure TForm1.Button2Click(Sender,
TObject);//计算按钮单击事件
var c:array of array of integer;
i,j,k:integer; s:string;
begin
setlength(c,high(a)+1,high(b[0])+1);
for i:=0 to high(a) do
4.2.2 应用 (3)
label2.Caption,=s;
end;
procedure
TForm1.Button2Click(Sender,TObject);
//计算按钮单击事件
var c:array of array of integer;
i,j,k:integer;
s:string;
begin
setlength(c,high(a)+1,high(b[0])+1);
for i:=0 to high(a) do
begin
for j:=0 to high(b[0]) do
begin
c[i,j]:=0;
for k:=0 to high(b) do
begin
c[i,j]:=a[i,k]*b[k,j]+c[i,j];
end;
s:=s+inttostr(c[i,j])+#32;
end;
s:=s+#13;
end;
label3.Caption,=s;
end;
procedure TForm1.FormCreate(Sender,
TObject);//窗体建立事件
begin
setlength(a,3,4);
setlength(b,4,3);
label1.WordWrap:=true;
//使标签可以换行输出
label2.WordWrap:=true;
label3.WordWrap:=true;
label1.AutoSize:=false;
label2.AutoSize:=false;
label2.AutoSize:=false;
end;
4.2.2 应用 (4)
2,分析由于矩阵数据输入相对麻烦,这里使用 random()函数产生随机数作为两个矩阵的输入数据,并且用动态数组来存储它们。在窗口建立事件中对这两个动态数组的大小进行了设置,并将三个标签组件的 wordwrap属性值设为真 (该属性为真表示可自动换行 )。在初始化按钮单击事件中,分别通过双重循环对两个数组进行了初始化操作,并且利用 label1和 lable2标签组件对它们进行了显示。在代码中,#32和 #13分别是空格和回车的 ASCII码,以便在标签组件中输出矩阵格式。在计算按钮单击事件中,使用了三个 for循环。其中 i变量控制了 a矩阵的行数,j变量控制了 b矩阵的列数,k变量则控制了 a矩阵的列数 (即 b
矩阵的行数 )。而新矩阵的第 i行 j列元素是第一个矩阵的第 i行和第二个矩阵的第
j列对应元素的乘积和。上述代码中,使用了 high(b[0])控制 j变量,试问能否改为 high(b[i] )?为什么?
3,运行结果运行以上程序结果如图 4-4所示。 图 4-4 矩阵运算结果