第5章 Delphi高级数据类型.docx
- 文档编号:14638744
- 上传时间:2023-06-25
- 格式:DOCX
- 页数:19
- 大小:369.49KB
第5章 Delphi高级数据类型.docx
《第5章 Delphi高级数据类型.docx》由会员分享,可在线阅读,更多相关《第5章 Delphi高级数据类型.docx(19页珍藏版)》请在冰点文库上搜索。
第5章Delphi高级数据类型
第5章高级数据类型
5.1枚举类型
5.1.1枚举类型的定义与变量声明
1.枚举类型的定义
(1)枚举类型使用一组有限的标识符来表示一组连续的整数常数,它的值是有限的。
枚举类型的定义格式如下:
type
<类型名称>=(<标识符1>,<标识符2>,…<标识符n>);
说明:
(1)type是系统的保留字,表示定义高级数据类型的开始。
例子:
Type
Colors=(Red,Yellow,Green,Cyan,Blue,Violet);
Suit=(Club,Diamond,Heart,Spade);
当属性值是枚举类型时,你可以从ObjectInspector显示的下拉列表框中选值,如图4.1所示。
(2)<标识符>表示该类型数据中的元素,圆括号中列出了该类型数据的所有取值,这些取值又称为枚举常量。
(3)同一个枚举常量不允许重复出现在同一个枚举类型定义中,也不允许同时出现在不同的枚举类型定义中。
2.枚举类型变量的声明
其声明的格式与其他类型变量的声明完全相同
例如:
VarC:
Color;
该语句声明了一个枚举类型Color的变量C。
5.1.2枚举类型的运算
1.使用函数
ObjectPascal为枚举类型定义了5个枚举函数,可以进行特殊的运算。
说明:
(1)枚举类型定义语句中列出的每一个枚举常量都对应一个唯一的序数(整数),称为枚举序数,在缺省情况下,列出的第一个枚举常量对应枚举系数0,以后依次为1、2、3…。
(2)在定义枚举类型时,排在某枚举常量前一位的枚举常量称为该枚举常量的前趋值,后一位的称为后继值。
第一个枚举常量没有前趋值,最末一个枚举常量没有后继值。
(3)由于每个枚举常量都对应一个枚举系数,所以枚举常量的序数可以进行算术运算,结果类型为整型。
但枚举常量之间不能直接进行算术运算,需要先转换为枚举序数。
2.关系运算
由于每个枚举常量对应一个唯一的序数,因此可以在枚举常量之间进行关系运算。
如在上述定义中,sun
5.2子界类型
5.2.1子界类型的定义
子界类型的定义格式如下:
type
<类型名称>=<常量1>...<常量2>;
说明:
(1)<常量1>表示子界类型的下界,即最小值,<常量2>表示子界类型的上界,即最大值。
子界的上下界必须属于相同的顺序类型,即它们应同时为整型、布尔型、字符型或同一个枚举类型。
(3)子界的上下界所属的数据类型即为子界的基类型,若子界的基类型为标准数据类型(整型
、布尔型、字符型),则子界的上、下界可以直接使用该类型常量,若子界的基类型为枚举类型,则必须先定义基类型(枚举类型),再定义子界类型。
(4)子界的上界必须不小于下界。
5.2.2子界类型变量的声明
其声明格式与其他类型变量的声明完全相同,如下面的代码声明了一个上述子界类型month的变量ml和workday类型的变量wl:
var
ml:
month:
wl:
workday:
5.2.3子界类型的运算
子界类型所允许的运算与其基类型所允许的运算相同,如基类型为整型子界类型变量可以进行算术、关系等运算,而基类型为枚举类型的子界类型变量仅能进行关系运算。
5.3集合类型
集合结构是指具有相同性质的对象的全体,构成集合的每个对象称为集合的元素。
注意:
(1)集合中的元素是互异的、无序的。
(2)集合元素个数不能超过256个。
(3)元素与集合的关系是“属于”或“不属于”,二者必取其一且仅取其一。
5.3.1集合类型的定义
其定义格式如下:
type
<类型名称>=setof<基类型>;
var
Letters1,Letters2,Letters3:
Letters;
begin
Letters1:
=['A','B','C'];
Letters2:
=['K'];
Letters3:
=[];
说明:
(1)<基类型>表示集合中各元素的类型,可以是字符型、布尔型、枚举型和子界等顺序类型,不能是整型、实型和其他的构造类型。
(3)若<基类型>为枚举类型或子界类型,则必须先定义该基类型,再定义集合类型。
(4)一个集合最多只能有256个元素。
另外,只有有序的类型才能跟关键字setof.
5.3.2集合变量的声明
其声明格式与其他类型变量的声明完全相同。
5.3.3变量集合的取值
集合变量不同其他变量,它不是一个单独元素,而是一系列元素的一集合。
集合变量的取值称为集合值,其一般表现形式如下:
[<元素1>,<元素2>,…,<元素n>]
如果集合类型的基类型有n个元素,则该集合类型变量的取值有2n个,包括一个空集合([])。
5.3.4集合类型的运算
集合类型的数据可以进行3大类运算:
一类是集合对集合的并、交、差运算,其结果为集合值;一类是集合的关系运算,其结果是
逻辑值;一类是元素对集合的“属于”运算,其结果也是逻辑值。
集合运算符具有不同的优先级,如表所示:
5.4数组与记录类型
数组类型(Array)是一些具有相同类型的元素按一定顺序组成的序列。
数组中的每一个数据元素都可以通过数组名来存取,它们被顺序安排在内存中的一段连续的区域中。
ObjectPascal提供的数组分为静态数组和动态数组。
而记录类型可以将不同的数据集中在一起,并作为一个整体进行操作。
5.4.1静态数组
静态数组在程序初始化时必须分配内存单元,明确其固定的大小和元素的数据类型。
1.一维静态数组
数组通常分为为一维、二维和多维数组,
定义一维静态数组类型的格式为:
type
<数组类型名>=array[<下标类型>]of<基类型>;
ObjectPascal允许的下标的类型为整数类型、字符类型、布尔类型、子界类型、枚举类型等,而元素的类型可以是任意的数据类
型,并且在同一数组中,所有元素的数据类型必须相同。
对于用户定义的数据类型作为下标类型,在使用之前必须声明。
要访问数组中元素,可以用数组名加方括号,方括号内是元素的下标值,方括号内的下标值必须符合数组类型中下标类型的定义,其类型必须与下标类型一致,其值在下标取值范围内。
而且下标也可以是表达式。
使用ObjectPascal提供的标准函数Low和high,可以返回一个数组的最小下标值和最大下标值,而函数Length可以返回数组的长度。
2.二维静态数组
二维数组是指一个一维数组中的元素类型又是一个一维数组,其一般形式为:
type
<数组标识符>=Array[<下标类型1>]ofArray[<下标类型2>]of<基类型>;
也可以把上述形式写成下面的形式:
type
<数组标识符>=Array[<下标类型1>,<下标类型2>]of<基类型>;
3.多维静态数组
多维静态数组的一般格式:
type
<数组标识符>=Array[<下标类型1>,<下标类型2>,…<下标类型n>]of<基类型>;
5.4.2动态数组
动态数组在定义和声明时仅指定数组的类型,而不指定数组的大小,只是在程序设计中为程序动态地开辟存储空间。
1.一维动态数组
一维动态数组的定义格式如下:
type
<数组类型名>=arrayof<基类型>
也可以在变量声明中直接声明动态数组,其格式为:
var
<变量名>:
arrayof<基类型>;
动态数组的声明中没有给出数组的下标类型,因此具有不确定的大小。
动态数组的大小通过调用标准过程Setlength来明确。
2.多维动态数组
声明多维动态数组采用递归定义的方式,如下:
type
<数组类型名>=arrayofarrayof…arrayof<基类型>;
var
<变量名>:
<数组类型名>
或者采用如下方式定义多维动态数组变量:
var
<变量名>:
arrayofarrayof…arrayof<基类型>;
多维动态数组声明后,使用Setlength过程设置动态数组的大小。
5.4.3记录类型
1.记录类型的定义
记录类型定义的格式如下:
▪type
▪<记录类型名>=Record
▪<域名表1>:
<类型1>;
▪<域名表2>:
<类型2>;
▪...
▪<域名表n>:
<类型n>;
▪end;
其中,<域名表>可以是多个合法的域名标识符,域名又称为字段名,<类型>可以是任意数据类型。
同一个记录类型中不能有同名的字段,而因为作用域的不同,记录内的字段名与记录外的标识符可以相同。
2.记录域的访问
由于记录类型中各字段的类型不同,所以不能同时访问记录的多个字段,而只能对记录的单个字段进行访问。
有两种方法:
(1)记录变量名限定
为了标识记录字段所属的记录变量,使用记录变量名进行限定,格式如下:
<记录变量名>.<字段名>
则为记录的单个字段赋值可以使用如下语句:
C1.Custid:
=1;
C2.IfPay:
=True;
(2)使用With语句
如果需要经常访问记录的字段,每次都用记录变量名进行限定非常麻烦,可以使用With语句加以简化。
With语句格式如下:
With<记录变量名>DO<语句>
其中,<语句>可以是简单语句,也可以是复合语句。
在<语句>中字段的访问不需要加记录变量名进行限定。
3.记录的变体部分
带有变体部分的记录类型的声明格式为:
▪type
▪<记录类型名>=Record
▪<域名表1>:
<类型1>;
▪<域名表2>:
<类型2>;
▪...
▪<域名表n>:
<类型n>;
▪Case<识别字段标识符>:
<识别字段类型>of
▪<常量表1>:
<字段列表1>;
▪<常量表2>:
<字段列表2>;
▪…
▪<常量表n>:
<字段列表n>;
▪end;
注意:
(1)Case前面的声明部分同平常的记录类型声明一样,但如果记录域中含有变体部分,则变体部分应位于记录域的最后。
(2)变体部分总识别字段标识符是可选的,省略时连同“:
”号一起省略,在同一记录域中必须是唯一的。
识别字段类型必须是顺序类型,如果是枚举或子界类型,则必须事先声明。
其中的字段列表i同普通的记录类型中域名表的声明相同。
其功能应用类似于选择结构中的Case语句。
5.5指针类型
指针是一种特殊的数据类型,指针类型(Pointer)的变量称为指针变量。
指针变量具有一般变量的三个基本要素,即变量名、变量类型、变量值,它与一般变量的不同,它是用来存放其他变量内存地址的一种变量。
5.5.1指针变量的声明
定义指针类型的语法如下:
type
<指针类型名>=^<基类型>
其中,<基类型>可以是基本数据类型,如整型、实型、字节型等,也可以是高级数据类型,如集合、数组、集合等类型。
5.5.2指针变量的赋值
为指针变量赋值的格式如下:
<指针变量名>:
=@<标识符>
其中,“@”操作符是个一元操作符,用于获取操作数的内存地址,@后面的操作数可以是变量、过程和函数等。
5.5.3无类型指针变量
无类型的指针是指指针变量在声明时没有指明基类型,无类型指针在声明中只使用Pointer,其声明格式如下:
var
<指针变量名>:
Pointer;
无类型的指针的作用是它可以指向任何类型,对于无类型指针,不能用指针变量符号后加^的形式来引用它的动态变量。
5.5.4字符指针类型
字符指针类型即Pchar数据类型,是一个指向以NULL字符结尾的字符串的指针。
主要用于与外部函数如在WindowsAPI中所用的函数兼容。
在Delphi7中,可以把一个字符串直接赋值给一个Pchar类型的变量。
5.5.5指针变量的动态使用
1.New过程和Dispose过程
如果不使用@运算符为指针变量赋值,则指针变量称为动态指针变量,动态变量在访问之前必须首先分配内存单元。
ObjectPascal提供了标准过程New,用来为动态变量分配内存单元,并把该单元的地址赋给指针变量,所分配单元的大小由指针所指的类型决定。
如果应用程序的堆栈中已没有足够的空间,将触发EoutOfMemory异常。
调用New过程的格式如下:
New(<指针变量名>);
调用过程New(p)之后,可以用“p^”表示一个整型的动态变量,对其进行操作。
当程序不再需要使用动态变量时,就调用标准过程Dispose删除New所创建的动态变量,并释放所分配的内存单元。
调用Dispose过程的格式如下:
Dispose(<指针变量名>);
2.GetMem过程和FreeMem过程
标准过程GetMem用于为动态变量申请一块指定大小的内存区域,并把该区域的起始地址赋给指针变量。
如果应用程序的堆栈中已没有足够的空间,将触发EoutOfMemory异常。
调用GetMem过程的格式如下:
GetMem(<指针变量名>,<区域大小>);
如果程序不再需要使用动态变量时,就调用标准过程FreeMem删除GetMem创建的动态变量,并释放所分配的内存单元。
调用FreeMem过程的格式如下:
FreeMem(<指针变量名>);
3.动态指针的应用举例
链表是一组元素的序列,在这个序列中,每个元素总是与它前面的元素相链接(第一个元素除外)。
这种链接关系可通过指针来实现。
链表中的元素通常称为节点,第一个节点称为表头,最后一个节点称为表尾。
指向表头的指针称为头指针,在这个头指针中存放着表头的地址。
节点一般用记录来描述,描述节点的记录至少含有两个域,一个域用来存放数据,该域的类型根据要存放的数据类型而定,称为值域;另一个域用来存放下一个节点的地址,称为指针域。
表尾不指向任何节点,其指针的值为Nil。
节点可以通过记录类型来描述,并且记录类型里包含一个指针域,链表节点的声明如下:
type
Node=record
data:
string;
next:
^Node
end;
var
Head:
^Node;//定义头节点变量Head;
或者采用如下方式:
type
Link=^Node
Node=record
data:
string;
next:
^Node
end;
var
Head:
Link;
5.6Varient类型
为了完全支持OLE,32位Delphi增加了Variant数据类型,本节将从宏观角度来分析这种数据类型。
实际上,Variant类型对Pascal语言有普遍而深入的影响,Delphi控件库中与OLE无关的地方也使用到这种类型。
Variant变量没有类型
一般说来,你可以用Variant变量存储任何数据类型,对它执行各种操作和类型转换。
需要注意的是:
这违反了Pascal语言的一贯原则,有悖于良好的编程习惯。
variant变量的类型检查和计算在运行期间才进行,编译器不会提示代码中的潜在错误,这些错误在进一步测试中才能发现。
总之,你可以认为包含variant变量的代码是解释性代码,正如解释性代码一样,许多操作直到执行时才能知道,这对代码运行速度会有很大的影响。
上面对Variant类型的使用提出了警告,现在来看看Variant类型究竟能干什么。
基本上说,如果声明了一个variant变量:
var
V:
Variant;
你就可以把各种不同类型的值赋给它:
V:
=10;
V:
='Hello,World';
V:
=45.55;
一旦得到一个variant值,你可以把它拷贝给任何兼容或不兼容的数据类型。
如果你把值赋给不兼容的数据类型,Delphi会力尽所能进行转换,无法转换则颁布一个运行时间错误。
实际上,variant变量中不仅包含了数据还包含有类型信息,并允许一系列运行时间操作,这些操作很方便,但运行速度慢且安全性差。
见例VariTest,它是上面代码的扩展。
窗体上有三个编辑框,一对按钮,第一个按钮的OnClick事件代码如下:
procedureTForm1.Button1Click(Sender:
TObject);
var
V:
Variant;
begin
V:
=10;
Edit1.Text:
=V;
V:
='Hello,World';
Edit2.Text:
=V;
V:
=45.55;
Edit3.Text:
=V;
end;
很有趣是不是?
你可以把一个值为字符串的variant变量赋给编辑框Text属性,还可以把值为整数或浮点数的variant变量赋给Text属性。
正如你在图10.1中所看到的,一切正常。
(图10.1)按Assign按钮后,例VariTest的输出结果
图10.1:
例VariTest的Assign按钮Click事件输出结果
更糟糕的是:
你还可以用variant变量计算数值,从第二个按钮的Click事件代码就可看到这一点:
procedureTForm1.Button2Click(Sender:
TObject);
var
V:
Variant;
N:
Integer;
begin
V:
=Edit1.Text;
N:
=Integer(V)*2;
V:
=N;
Edit1.Text:
=V;
end;
至少这种代码带有一定危险性,如果第一个编辑框包含了一个数字,那么一切运行正常;如果不是,将会引发异常。
这里再重申一遍,如果不到万不得以,不要随便使用Variant类型,还是应坚持使用传统的Pascal数据类型和类型检查方法。
在Delphi和VCL中,variant变量主要是用于OLE支持和数据库域的访问。
Variant类型内部结构
Delphi中定义了一个variant记录类型,TVarData,它与Variant类型有相同的内存布局。
你可以通过TVarData访问variant变量的实际类型。
TVarData结构中包含了Variant类型信息(由Vtype域表示)、一些保留域及当前值。
VType域的取值包括OLE自动化中的所有数据类型,这些类型通常叫OLE类型或variant类型。
以下是variant类型的完整列表,按字母顺序排列:
∙varArray
∙varBoolean
∙varByRef
∙varCurrency
∙varDate
∙varDispatch
∙varDouble
∙varEmpty
∙varError
∙varInteger
∙varNull
∙varOleStr
∙varSingle
∙varSmallint
∙varString
∙varTypeMask
∙varUnknown
∙varVariant
你可以在Delphi帮助系统的variants主题下找到这些类型的说明。
还有许多操作variant变量的函数,你可以用它们进行特定的类型转换,或通过它们获取variant变量的类型信息(例如VarType函数),当你用variant变量写表达式时,Delphi会自动调用这些类型转换和赋值函数。
另外还有操作variant数组的例程,你可以通过帮助文件的Variantsupportroutines主题了解相关内容。
Variant类型运行很慢!
Variant类型代码运行很慢,不仅数据类型转换如此,两个值为整数的Variant变量相加也是如此。
它们几乎跟VisualBasic这种解释性代码一样慢!
为了比较Variant变量和整型变量的运行速度,请看例VSpeed。
程序中设置了一个循环,记录运行时间并在进程条中显示运行状态。
下面是基于variant类型的一段代码,基于整型的代码与此相似:
procedureTForm1.Button1Click(Sender:
TObject);
var
time1,time2:
TDateTime;
n1,n2:
Variant;
begin
time1:
=Now;
n1:
=0;
n2:
=0;
ProgressBar1.Position:
=0;
whilen1<5000000do
begin
n2:
=n2+n1;
Inc(n1);
if(n1mod50000)=0then
begin
ProgressBar1.Position:
=n1div50000;
Application.ProcessMessages;
end;
end;
//wemustusetheresult
Total:
=n2;
time2:
=Now;
Label1.Caption:
=FormatDateTime(
'n:
ss',Time2-Time1)+'seconds';
end;
记时这段代码值得一看,因为你可以把它用到任何类型的性能测试中。
正如你所看到的,程序用Now函数获取当前的时间,用FormatDateTime函数格式化时间差,输出结果以分("n")和秒("ss")表示。
除此之外,你可以用WindowsAPI的GetTickCount函数,该函数能精确显示操作系统启动后至当前的毫秒数。
从上例可见两者的速度差异非常之大,以至于不用精确记时也能看到这种差异。
图10.2是在本人计算机上运行程序看到的结果。
当然运行结果取决于运行程序的计算机,但是两者的数值比不会有太大变化。
图10.2:
例Vspeed中整型与Variant类型的计算速度差异
结束语
Variant类型与传统Pascal数据类型差别很大,所以本章以短小篇幅单独阐述了Variant类型的有关内容。
尽管Variant类型主要用于OLE编程,但用来写一些潦潦草草的程序倒也便利,因为不用考虑数据类型,不过正如以上所述,这样做会影响程序执行速度。
通过前面各章我们已经介绍了绝大部分的语言特征,下一章将讨论程序的总体框架和单元模块。
5.7Stream类型
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 第5章 Delphi高级数据类型 Delphi 高级 数据类型