Object Pascal 数据类型
Object Pascal 语言的最大特点是对数据类型的要求非常严谨。传递给过程或函数的参数值必须与形参的类型一致。在Object Pascal 语言中不会看到像C 语言编译器提示的“可疑的指针转换”等警告信息。由于Object Pascal 语言对数据类型比较严谨,因此它会对代码进行严格检查,以确保不会出现错误。变量是程序代码中代表一个内存地址的标识符,那么该地址的内存内容就可以在程序代码执行时被改变。每个变量都有一个名字和数据类型,名字可以用来引用变量,数据类型决定变量的取值范围。
Object Pascal 语言提供了非常丰富的数据类型,即简单类型(Simple)、字符串类型(String)、结构类型(Struct)、指针类型(Pointer)、函数和过程类型(Procedural)和变体类型(Variant)等。
(1)简单类型(Simple)
简单类型包括实数类型(Real)和有序类型(Ordinal)。有序类型又包括整数类型、字符类型、布尔类型、枚举类型和子界类型等。它们的关系如图4-1 所示。
图4-1 简单类型关系图
有序类型是一个有序数的集合。在每一种有序类型中,任何一个该类型的元素都有一个唯一的先行数(第一个除外)和唯一的后继数(最后一个除外)。而且,每个值均有一个确定的序号。对整型数而言,该序号就是该整数的本身。而其它的有序类型(子界类型除外),第一个值的序号是0,第二个是1,依此类推。如果某个有序类型的值为N,则其先行数为N-1,后继数为N+1。Object Pascal预先定义了一些函数,专门用于处理有序类型的表达式和变量,表1-4.1列出了最常用的几个。
Delphi里面的有序类型指的是在ASCII码范围内的字符或者数字。(摘录)
表1-4.1 常用函数
函数 | 参数 | 返回值 | 备注 |
Ord | 有序类型表达式 | 有序类型表达式值的序号 | 不能带Int64参数 |
Pred | 有序类型表达式 | 有序类型表达式值的先行数 | 不能在拥有Write过程的特性上使用 |
Succ | 有序类型表达式 | 有序类型表达式值的后继数 | 不能在拥有Write过程的特性上使用 |
High | 有序类型标识符或变量 | 该类型中序号最大的值 | 也可在short-string类型或数组 |
Low | 有序类型标识符或变量 | 该类型中序号最小的值 | 也可在short-string类型或数组 |
Inc | 有序类型标识符或变量 | 增加一个有序类型变量的值 | |
Dec | 有序类型标识符或变量 | 减少一个有序类型变量的值 |
例如,表达式 High(Byte)将返回255,这是因为Byte类型的序数最大为255。表达式Succ(2) 将返回3,这是因为 3紧跟在2的后面。标准过程Inc和Dec用于增加或减少一个有序类型变量的值,例如:Inc(I)等价于执行 I := Succ(I)如果I是一个整数类型,还等价于执行: I := I + 1。
① 整数类型
在Object Pascal中,Integer类型是所有有符号整数的统称。实际上,整数类型可以分为基本整数类型(Fundamental type)和一般整数类型(generic type)。一般整数类型(generic type)包括Integer和Cardinal两种。在实际编程时,请尽量区分这两种,因为底层CPU和操作系统对结果进行了优化。
整数类型包括的类型,如表1-5 所示。
表1-5 整数类型
整数类型 | 范围 | 备注 |
Integer | -2147483648~2147483647 | 32 位带符号数 |
Cardinal | 0~4294967295 | 32 位无符号数 |
Shortint | -128~127 | 8 位带符号数 |
Smallint | -32768~32767 | 16 位带符号数 |
Longint | -2147483648~2147483647 | 32 位带符号数 |
Int64 | -2^63~2^63 | 64 位带符号数 |
Byte | 0~255 | 8 位无符号数 |
Word | 0~65535 | 16 位无符号数 |
Longword | 0~4294967295 | 32 位无符号数 |
一般整数类型的实际范围和存储格式随着Object Pascal的不同实现而不同,但通常根据当前CPU和操作系统来采取最佳的操作方式。一般整数类型是最常用的整数类型,可以充分利用CPU和操作系统的特性,因此程序中应当尽量使用一般整数类型。基本整数类型独立于操作系统和CPU,只有当应用程序的数据范围或存储格式特别需要时,才使用基本整数类型。通常情况下,整数的算术运算结果为Integer类型,等价于32位的Longint类型。只有当操作数存在 Int64类型时,才返回Int64类型的值。因此,下面的代码将产生错误的结果:
var I: Integer; J: Int64; ... I := High(Integer); J := I + 1;
在这种情况下,要取得一个Int64的值,必须进行类型转换:
J := Int64(I) + 1;
注意:绝大多数例程在遇到Int64时都把它转换为32位。但例程High,Low,Succ,Pred,Inc,Dec,IntToStr和IntToHex则完全支持Int64参数。Round,Trunc,StrToInt64,和StrToInt64Def函数可以返回Int64类型的结果。
② 字符类型
字符类型中Char类型设计只存储一个字符。一个字符占一个字节,因此Char数据类型可以存储256个不同的字符,其对应的整数为0到255。除了Char数据类型外,Delphi还提供了Char类型的扩展,即AnsiChar和WideChar型。字符类型3 种字符变量如表1-6 所示。
表1-6 字符类型
字符类型 | 取值 | 备注 |
AnsiChar | 8位 | 根据扩展ANSI 字符表排列 |
WideChar | 16位 | 根据Unicode 码排列,前256 个Unicode 字符同ANSI 字符相同 |
Char | 8位 | 目前,对应AnsiChar。但Delphi将来的版本可能对应于WideChar。 |
Ansi字符集是扩展的ASCII字符集,仍然占一个字节。目前,Char对应AnsiChar,但Borland公司在将来的Delphi版本中可能使Char对应WideChar。
WideChar用来支持泛字符集(Unicode)。Unicode字符占用两个字节,可以有65536种不同的取值,可以表达现代计算机中使用的世界上所有的字符,包括图形符号和用于出版业的特殊符号等。UniCode字符集的前256个字符对应着ANSI字符。如果你把一个AnsiChar字符放到WideChar字符类型的变量中,WideChar字符类型变量的高字节将全部置为0,AnsiChar字符存放到WideChar字符类型的变量的低字节中。
注意:Windows NT全面支持Unicode字符号集,但Windows 95却不同。如果你希望书写的程序同时能在两种系统上运行,必须使用SizeOf()函数,以确定字符占多少字节。
③ 布尔类型
布尔类型包括4 种,分别为Boolean、ByteBool、WordBool 和LongBool。Boolean 和ByteBool 各占用一个字节,WordBool 占用2个字节,LongBool 占用4 个字节。
Boolean 的值只能是0(False)或1(True),ByteBool、WordBool 和LongBool 可以是有符号的整数,为0 值代表False,非0 值代表True。
最常用的是Boolean 类型,而ByteBool、WordBool 和LongBool 类型是为了与其他语言和Windows环境兼容,因为Windows 的API 在返回一个布尔值时,其值可能是一个两字节的有符号整数。如果试图把返回值赋给Boolean 类型的数据,编译器认为类型不匹配,若是进行类型强制转换,可能使返回值的有效数据被截断。
④ 枚举类型
枚举类型是由一组有序的标识符组成的,它列出了所有这种类型可以包括的值。例如一个星期有7 天,程序中分别用0、1、2、3、4、5、6 来表示一个星期的每一天,但在程序中很不直观。下面就是声明了一个枚举类型的代码:
type DayOfWeek=(Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);//变量名=(枚举列表)
其中列表中的标识符彼此之间用逗号分隔,它列出了枚举类型所能取值的范围。Object Pascal 规定,第1 个标识符的值为0,第2 个标识符的值为1,依次类推。
为了引用此枚举类型,必须声明一个DayOfWeek 类型的变量,例如:
var Days:DayOfWeek;
这样就声明了一个DayOfWeek 类型的枚举变量Days,Days 的值总是标识符列表中的一个。
为了简化程序,还可以将类型声明和变量声明合二为一,例如:
var Days:(Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);
注意:枚举的元素只能是标识符,标识符的命名规则必须符合Object Pascal 关于标识符的规定。例如不能以数字打头,标识符不能是保留字,同一个枚举元素不能同时出现在多个枚举中。
在声明枚举类型和枚举变量时,请注意以下几点:
1)枚举的元素只能是标识符,标识符的命名必须符合 Pascal关于标识符的规定,例如下面的声明就是错误的:
type TDays=(0,1,2,3,4,5,6,7);
2)同一个枚举元素不能出现在多个枚举类型中。例如下面的声明就是错误的:
type TColors1=(Red,Blue,Green,White,Black); TColors2=(Yellow,Lime,Silver,Green);
两个类型变量中都有Green元素,是不允许的。
3)不能直接用枚举类型中的元素参加运算,例如,下面的语句就是错误的:
X:=Red+Blue;
但是,可以用某个枚举类型中的元素对枚举变量赋值,例如,下面的语句:
DayOfWeek:=Monday;
⑤ 子界类型
子界类型是下列类型中某范围内的值,即整型、布尔型、字符型或枚举型。如果需要限制一个变量的取值范围时,子界类型是一个好的选择。例如:
type Month=1..12; Letters=’A’.. ’H’; DayOfWeek=(Sunday,Monday,Tuesday,Wednesday,Thursday,Friday,Saturday);//枚举型 Days=Monday..Friday; //一个DayOfWeek 型的子界
跟枚举类型一样,子界类型是不能直接参加运算的,必须声明一个子界类型的变量。
在声明子界类型和子界类型变量时,请注意以下几点:
1)上界常数和下界常数必须是同一类型,且都是有序类型。
2)子界类型变量具有宿主类型数据的所有运算特性,但运算的结果必须在范围内。
3)上界常数和下界常数可以是表达式。例如:
const X = 10; Y = 50; type Color = (Red, Green, Blue); Scale = X * 2..X * 20;
⑥ 实型
实型不是有序类型,因为它的值域是无限的,实型的数据也叫实数,实型用于表示通常所说的浮点数。Object Pascal 语言中的实数数据(Real)类型如表1-7 所示。
表1-7 实数类型
实数类型 | 范围 | 有效位数 | 字节数 |
Real48 | 2.9*10^-39~1.7*10^38 | 11~12 | 6 |
Single | 1.5*10^-35~3.4*10^38 | 7~8 | 4 |
Double | 5.0*10^-324~1.7*10^308 | 15~16 | 8 |
Extended | 3.6*10^-4951~1.1*10^4932 | 19~20 | 10 |
Comp | (-2^63)+1~(2^63)-1 | 19~20 | 8 |
Currency | -922337203685477.5808~922337203685477.5807 | 19~20 | 8 |
Real | 5.0*10^-324~1.7*10^308 | 15~16 | 8 |
其中常用的实数类型是Real、Single、Double。
(2)字符串类型(String)
Delphi在处理字符串时,提供了多种方式,表1-7.1是Delphi使用的字符串类型。
表1-7.1 字符串类型
类型 | 最大长度 | 存储的字符 | 是否以Null结尾 |
ShortString | 255个字符 | AnsiChar | 否 |
AnsiString | ~2^31个字符 | AnsiChar | 是 |
String | 或者255或者~2^31个字符 | AnsiChar | 都可能 |
WideString | ~2^30个字符 | WideChar | 是 |
从上表可知,Delphi主要支持两种类型的字符串: ShortString和AnsiString。WideString类似于AnsiString,只是其存储的字符为WideChar。
ShortString数据类型定义的是长度在1到255之间动态长度字符串。像数组一样,单个字符可以通过引用它们的索引进行存取。位于0的字节存储了代表字符串当前所赋值长度的数值(只能通过关闭范围检查才能访问)。ShortString数据类型主要是为了能和Delphi 1.0和Borland Pascal的早期版本兼容。
AnsiString(又称为long String或huge String)数据类型的定义是动态分配的,长度几乎可以无限的(仅受可用内存限制)以NULL结尾的字符串。AnsiString中的字符由AnsiChar数据类型的字符组成。建议最好使用AnsiString数据类型。这是因为AnsiString数据类型的变量是动态分配的,当把一个更长的字符串放入AnsiString数据类型的变量时,Delphi会从新为该变量申请空间。如果要显式地改变字符串的长度,可以使用SetLength() 函数来分配一块恰当的内存;使用AnsiString数据类型的另外一个优点是,AnsiString字符串是以NULL结尾,即在最后一个字符之后自动加上一个NULL字符表示字符串结束,与操作系统的大多数函数例程兼容,例如Win32 API,从而在调用操作系统函数例程时更加方便,不需要使用StrPCopy()来将以Pascal风格的字符串转换为以NULL结尾的字符串。Delphi VCL构件的所有特性、事件使用AnsiString来传递参数,以简化、统一VCL和API之间的接口。
String既可以是SHortString类型也可以是AnsiString类型,缺省是AnsiString类型。例如,如果你像下面那样定义字符串:
var S: String;// S is an AnsiString
则编译器假定你要创建一个AnsiString数据类型变量。
使用$H编译命令可以改变缺省定义。当在程序中把编译开关$H的状态改为{H-}时,String缺省是ShortString类型;当在程序中把编译开关$H的状态改为{H+}时,String缺省是AnsiString类型。例如:
var {$H-} S1: String; // S1 is a ShortString {$H+} S2: String; // S2 is an AnsiString
如果定义中指明了长度(最大为255),则String为ShortString。例如:
var S: String[63]; // S是一个 长度为63的ShortString变量。
Object Pascal 的字符串类型分为短字符串和长字符串两种。
短字符串,指字符串长度最大不超过255个字符的字符序列。当编译开关的状态为{$H-}时,用保留字String 声明的是一个短字符串,不管编译开关$H 处于什么状态下,用ShortString 声明的变量总是短字符串类型。代码如下:
var MyStr1:ShortString; MyStr2:String[200];
保留字String 后用方括号括起来的无符号整数,表示字符串的最大长度是200。当把一个字符串赋给一个短字符类型的变量时,超过其最大长度的将被截掉。
短字符串中的每一个字符都可以通过字符串名加字符索引来访问。索引是从0 开始的,但索引0处存放的是字符串的实际长度,如下两行代码的功能是一样的,索引1 处才是真正的字符开始处。
Ord(Str[0]); //Str 代表短字符串 Length(Str);
短字符串的长度是动态改变的,可以使用Low 和High 函数取得字符串的最小序号和最大序号,Low 的返回值是0,High 的返回值是所声明的字符串的最大长度。长字符串的长度几乎是无限制的,理论上可以达到2GB,实际上只会受计算机内存的限制。要声明一个长字符串,可以用预定义的标识符AnsiString 或把编译开关$H 改为{$H+}后用保留字String 声明。长字符串在声明以后自动被初始化为空字符串。长字符串中的每个字符,是从索引1 开始的,长字符串没有索引0,要取得长字符串的实际长度,只能调用Length()函数。长字符串在它最后一个字符后自动加一个NULL 字符表示字符串结束(NULL 字符并不是长字符串的一部分),这一点与字符指针类型(PChar)类似,因此将一个长字符串转换为PChar 类型很方便,
语法为:
PChar(Str),
其中Str 代表长字符串。
长字符串被转换为一个指针,指向字符串的每一个字符,即使长字符串是空的,强制转换后总是能够获得一个指向以NULL 字符结束的字符中的指针。Object Pascal 允许字符串和长字符串同时出现在一个表达式中,编译器会自动根据需要进行转换。注意:字符串变量必须用一对单引号括起来,如果字符串本身就有单引号,这个单引号要用两个连续的单引号表示,如:’His name is ’’Jim’’’。
(3)结构类型(Struct)
结构类型在内存中存储一组相关的数据项,而不是像简单数据类型那样单一的数值。结构数据类型包括:集合类型、数组类型、记录类型、文件类型、类类型、类引用类型和接口类型等。
① 集合类型
集合类型是一群具有相同类型的元素的组合,这些类型必须是有限类型,如整型、布尔型、字符型、枚举型和子界类型。
集合类型的声明方法,请看以下代码:
type Set1=set of ’A’.. ’Z’; //基类型是子界型 Set2=set of Char; //基类型是字符型 Set3=set(Jan,Feb,Mar,Apr); //基类型是枚举型
Object Pascal 语言提供了几个用于集合的运算符,用这些运行符可以判断集合与集合之间的关系,例如对集合增删元素,求交集运算等。
一个集合类型的变量的值实际上是它的基类型的一个子集,可以为空集。一个集合最多可有256个元素。因此下面的集合定义是错误的:
type SET1= Set Of Integer;
这是因为Integer集合的元素个数远远大于256。Pascal使用方括号来表示集合,例如:
[Democrat];
表示只含Democrat的集合。
一个集合可以拥有0个元素,这时称之为空集,用两个方括号表示,其中什么也没有。对于集合类型变量,你可以进行+,-,=,*(并),IN等运算。见下表1-7.2。
表1-7.2 集合类型运算
操作符 | 描述 | 举例 |
+ | 往一个集合中添加元素 | Aset:=Aset+AnotherSet; |
- | 从一个集合中去除元素 | Aset:=Aset-AnotherSet; |
* | 去除两个集合中都没有的元素 | Aset:=Aset*AnotherSet; |
In | 测试元素 | Bool:=AnElement in Aset |
下面是集合运算的例子:
Voter:=[HighOption]; Voter:=Voter+[Democrat]; Voter:=Voter+{male}; Voter:=Voter-[HighOption]; If HighOption in Voter then
SendtheVoterFlowers;
并运算:A集合+B集合中不在A集合中的元素,并生成新的集合C
[A,B,C] + [B,C,D] = [A,B,C,D]
交运算:排除A集合与B集合中不相同的元素,并生成新的集合C
[A,B,C] * [B,C,D] = [B,C]
差运算:在A集合中除去B集合中与A集合中相同的元素,生成新的集合C
[A,B,C] - [B,C,D] = [A]
赋值运算与简单类型一样,声明了一个集合类型的变量后,要为这个变量设定一个明确的值,例如:
var TInt1,Tint2:set of 1..10;
以上代码虽然声明了两个集合变量,但变量TInt1 和TInt2 并不是代表1-10 组成的集合,必须对它们赋值:
TInt1:=[1,3,5,7,9]; TInt2:=[2..5,8..10];
这样TInt1 就是由1、3、5、7、9 组成的集合,TInt2 就是由2、3、4、5、8、9、10 组成的集合。
关系运算,就是对集合类型的运算,遵循数学中关于集合运算的规则,运算的结果是布尔值。集合的关系运算就是判断两个集合相互之间的关系,关系运算符包括:=(等于)、<>(不等于)、>=(大于等于)、<=(小于等于)。
集合A 等于集合B,是指两个集合中的元素完全一样(不管元素的排列顺序如何),否则就是不相等,如下:
[1,2,3,4]=[2,4,1,3] [1,2,3,4]<>[5,8,9]
如果集合B 中的元素在集合A 中都可以找到,那么称集合A 大于等于集合B,反之称集合B 大于等于集合A,如下:
[1,2,3,4]>=[1,2,3]
注意:Object Pascal 中不能用>或<对集合类型进行关系运算。因为集合类型不是有序类型,所以不能用Ord、Pred、Succ 等函数。
in 操作符使用
procedure TForm4.Button8Click(Sender: TObject); var SetEnum := [one, Two]; begin if One in SetEnum then begin ShowMessage('Exclude函数执行失败'); end else if Two in SetEnum then begin ShowMessage('SetEnum - [Two]执行失败'); end else if not (Three in SetEnum) then begin ShowMessage('SetEnum + [Three]执行失败'); end else if not (Four in SetEnum) then begin ShowMessage('Include函数执行失败'); end else if not (ten in SetEnum) then begin ShowMessage('Include函数执行失败'); end else begin ShowMessage('执行成功!'); end; end;
② 数组类型
数组是一种数据类型数据的有序集合,是代表一定数量具有相同类型变量的一种数据类型。Object Pascal数组可与任何简单数据类型或字符串类型等一起使用。数组可用于声明一个简单变量或作为一个记录类型定义的组成部分。
(1)数组的定义
声明一个数组变量,要求你提供一个标识符,使用array保留词,在方括号中指定数组的界限,并指定编译器数组将用于存储什么类型,例如:
Var Check:array[1..100] of Integer;
范围标点‘..’用于表示Check是一个有100个整数的数组,这些整数从1到100编号。范围编号是一个子界类型,可以是0,也可以是正数或负数,或者字符,或其它有序类型。
下面是声明数组类型及数组类型变量的举例:
Type TCheck = array [1..100] of Integer; Var CheckingAccount:TCheck;
上面是先定义数组类型,然后定义数组变量。其实上,也可以同时定义类型、变量,例如:
var Kelvin:array[0..1000] of Temperatures; TwentiethCentury: array[1901..2000] of Events; LessThanzeroo: array[-999..-400] of Shortint; DigiTValues:array ['0'..'9'] of Byte; SecretCode:array[''A'..'Z'] of char;
访问数组中的元素很简单,只要在标识符后面的方括号中给出指定的元素的索引号即可。例如:
Check[5]:=12; J:=9; Check[J]:=24;
要访问数组中的所有元素,可以使用循环语句。例如 :
For J:=1 to 10 do
Check[J]:=0;
(2)多维数组
上面介绍的是一维数组。实际上,数组可以是多维的。例如,如果你想编写一个数组来容纳一张电子表格中的值,那么就可以使用2维数组。下面的例子说明如何使用2维数组定义一个有20行和20列的表格:
Type Ttable = array[1..20,1..20] of Double; Var BigTable:Ttable;
要将2维数组中的所有数据初始化,可以使用如下语句:
var Col,Row:Intger; for Col:=1 to 20 do for Row:=1 to 20 do BigTable[Col,Row]:=0.0;
使用多维数组时,要记住的一件事是数组为每维所占据的RAM数都呈幂级数增加。例如:
Aline:Array[1..10] of byte;占用10个字节 AnArea:Array[1..10,1..10] of byte;占用10*10=100个字节 Avloume:Array[1..10,1..10,1..10] of byte;占用10*10*10=1000个字节
(3)字符数组
前面介绍的字符串,实际上就是一个1维字符数组,只不过Pascal对字符串类型作了特殊的准许,你可以把它看作一个整体。字符串类型本质上等同于下列类型:
type StringType:array[0..255] of char;
虽然你可以当一个字符串看待,但它仍然保持其数组的特性。例如在定义一个字符串类型变量时,你可以说明字符串的大小,就像你定义字符数组的大小一样。下面是几个字符串类型定义:
type MyString:string[15]; BigString:string; LittleString:string[1];
上面语句定义MyString类型包含15个字符,LittleString包含1个字符,BigString没有说明大小,就取字符串包含字符的最大个数255。然后你可以定义这些类型的变量,就像使用其它类型一样:
Var MyName:MyString; Letter,Digit:LittleString;
你可以对字符串变量进行赋值:
MyName:='Frank P.BorLand';
因为MyName长度为15,因此只能容纳15个字符。如果执行下面语句:
MyName:='Frank P.Borland and ...';
则MyName变量中只存有FranK.P.Borlan其余部分被舍弃。
为了取得字符串中的一个字符,可以按如下方法进行:
AChar:=MyName[2];
但是,如果索引大于字符串变量的长度,则结果不可知。例如:
AChar:=MyName[16];
则AChar将被设置为某个不确定的字符,换句话说,就是废字符。
在字符串类型的数组中,字符串的第一个位置[0]包含有字符串的长度,因此数组的实际长度比该字符串长度大1个字节。你可以使用Length函数或下面的代码来得到一个字符串的长度:
L:=Ord(String[0]);
(4)数组类型常量
数组类型常量的每个字段都是类型常量,一个数组类型常量由括号括起来的类型常量组成,不同类型常量用逗号隔开。像简单类型常量一样,数组类型常量用来定义一个数组常量,下面是一个例子。
type TStatus = (Active, Passive, Waiting); TStatusMap = array[TStatus] of string; const StatStr: TStatusMap = ('Active', 'Passive', 'Waiting');
上面的例子首先定义一个数组TStatusMAp,然后定义一个数组常量StatStr。该数组常量的目的是把TStatus类型的值转化为对应的字符串。下面是数组常量StatStr元素的值:
StatStr[Active] = 'Active' StatStr[Passive] = 'Passive' StatStr[Waiting] = 'Waiting'
数组常量的元素类型可以是除文件类型以外的任何类型。字符数组类型常量既可以是字符也可以是字符串,例如:
const Digits: array[0..9] of Char = ('0', '1', '2', '3', '4', '5','6', '7', '8', '9');
该数组常量也可以表示为:
const Digits: array[0..9] of Char = '0123456789';
初始化字符数组类型常量的字符串长度可以小于数组类型的定义长度,例如:
var FileName: array[0..79] of Char = 'TEST.PAS';
这时数组余下的字符空间自定置NULL(#0),因此数组也变成了一个以NULL结尾的字符串。
多维数组类型常量的定义采用括号的形式,每一维用括号括起,不同维及不同元素常量之间用逗号隔开。最里面的常量对应最右面的维数。例如:
type TCube = array[0..1, 0..1, 0..1] of Integer; const Maze: TCube = (((0, 1), (2, 3)), ((4, 5), (6, 7)));
Maze常量数组各元素的值为:
1
2
3
4
5
6
7
8
|
Maze[ 0 , 0 , 0 ] = 0 Maze[ 0 , 0 , 1 ] = 1 Maze[ 0 , 1 , 0 ] = 2 Maze[ 0 , 1 , 1 ] = 3 Maze[ 1 , 0 , 0 ] = 4 Maze[ 1 , 0 , 1 ] = 5 Maze[ 1 , 1 , 0 ] = 6 Maze[ 1 , 1 , 1 ] = 7 |
(5)开放式数组
所谓开放式数组,是指数组作为形参传递给过程或函数时其长度是可变的,这样在调用过程或函数时,可以传递不同长度的数组作为实际参数。开放式数组在过程或函数中作为形参可以定义为:
array of T
这里T是数组的元素类型标识符,实际参数必须是T类型的变量或元素类型为T的数组变量。在过程或函数内形参的作用可看作为下面的数组:
array[0..N - 1] of T
这里N是实参中元素的个数。实际上实参的上下界被映射到0到 N-1。如果实参是类型T的简单变量,则它被看成为只有类型T元素的数组。
开放数组只能以开放数组参数或一个未定义变量参数的形式传递到过程或函数。开放数组可以作为数值参数、常数参数或变量参数,并与这些参数具有同样的语法规则。作为形式参数的开放数组不允许整体赋值,只能访问它的元素。并且对元素的赋值不影响实参。当开放式数组作为数值参数时,编译器将在内存中开辟一块区域存放实参的拷贝,等过程或函数退出后再释放这块区域,这样当实参是个很大的数组时,可能会发生栈溢出的问题。在使用开放数组参数时,可以使用Low函数获得当前最小下标(不过总是为0),使用High函数获得当前最大下标,使用SizeOF函数获得当前数组大小。下面是一个例子,演示了开放式数组的使用。
Var X1:array[1..10] of Double; X2:array[1..30] of Double; {Clear过程对一个Double数组的各元素清0,SUM过程计算一个Double数组的各元素之和。两个过程的参数都是开放式数组。} procedure Clear(var A: array of Double); var I: Integer; begin for I := 0 to High(A) do A[I] := 0; end; function Sum(const A: array of Double): Double; var
I: Integer;
S: Double; begin S := 0; for I := 0 to High(A) do
S := S + A[I]; Sum := S; end; begin Clear(X1); Clear(X2); Sum(X1); Sum(X2); end.
当开放式数组的元素类型为Char时,实参可以是一个字符串常数。例如:
procedure PrintStr(const S: array of Char); var I: Integer; begin for I := 0 to High(S) do if S[I] <> #0 then
Write(S[I]) else
Break; end.
下面是合法的过程调用语句:
PrintStr('Hello world'); PrintStr('A');
(6)动态数组
在Delphi中,除了定义静态数组外,还可以定义动态数组。动态数组只需说明数组的类型信息(包括数组的维数和数组元数的类型),但不需要定义元素的个数。例如:
A: array[1..100] of string;//静态数组 B: array of integer//动态数组 C: array of array of string;//动态数组
这里A是静态数组,B是一维的整数动态数组,C是二维的字符串动态数组。动态数组没有固定的长度。相反,当为动态数组赋值或使用SetLength过程时,动态数组的内存空间将重新分配。动态数组的定义形式是:
array of baseType
例如:
var MyFlexibleArray: array of Real;
定义了一个类型为实数型的一维动态数组。注意,声明语句并没有为MyFlexibleArray分配内存。要为动态数组分配内存,需要调用SetLength过程。例如:
SetLength(MyFlexibleArray, 20);
上面语句分配20个实数,标号从0到19。
动态数组的标号是整数类型,标号总是从0开始。使用Length,High和Low函数可以取得有关动态数组的特性。Length函数返回数组中元素的个数。High函数返回数组的最大标号,Low返回0。
③ 记录类型
记录是程序可以成组访问的一群数据的集合。例如下列代码,说明了一个记录类型的定义方法:
type TStudent=record ID:Integer; Name:String[10]; Age:Integer; Memo:String[100]; end;
记录包含可以保存数据的域,每一个域都有一个数据类型。上面代码中声明的TStuednt 类型有4个域。用下面的方法对记录类型的变量进行声明与使用:
var Stu:TStudent; with Stu do //以下代码为记录赋值,也可以只给记录的单个域赋值 begin ID:=1; Name:= 'Jim'; Age:=18; Memo:= 'He is a good student.'; end;
(4)指针类型(Pointer)
指针是一个表示内存地址的变量。指针类型在任何语言中都比较难理解,也是一种比较灵活的数据类型。用以下形式定义指针变量:
type 指针类型标识符=^基类型;
下面列举一个对指针操作的例子,代码如下:
program Point; //指针示例工程 {$Apptype console} type PInt=^Integer; //定义指针类型 var I,K:Integer; PI:Pint; //整形指针 P:Pointer; //无类型指针 begin I:=3; K:=5; PI:=@K; //整形指针指向整形数据 writeln('K=',PI^); P:=@I; K:=Integer(P^); //无类型指针指向整形数据 writeln('K=',K); PI:=P; //指针之间赋值 writeln('PI^:',PI^); readln; end.
运行结束,如图1-2 所示。
图1-2 指针示例工程显示结果
“@”运算符放在变量前面,获取的是变量的地址,并可以将地址赋值为相同数据类型的指针。把“^”运算符放在数据类型的前面,可以定义该类型的一个指针类型;放在一个指针的后面,可以获得该指针所指地址空间的内容。
例如在“Point”中,定义了一个可以指向任何数据类型的无类型指针P,但使用过程中要注意类型的转换,不可以直接将所指地址中的内容直接赋值给其他类型变量。
Object Pascal 语言中定义了一些标准指针,可以直接调用,如表1-8 所示。
表1-8 Object Pascal 语言中的标准指针
指针类型 | 所指向变量的类型 |
PString,PAnsiString | AnsiString |
PByteArray | ByteArray |
PCurrency | Currency |
PExtended | Extended |
POleVariant | OleVariant |
PShortString | ShortString |
PVariant | Variant |
PVarRec | TVarRec(调用时需引用System 单元) |
RTextBuf | TextBuf(调用时需引用SysUtils 单元) |
PWideString | WideString |
PWordArray | TWordArray(调用时需引用SysUtils 单元) |
(5)函数和过程类型(Procedural)
Delphi 是面向对象和事件驱动的,因此以前那种自上而下的结构化设计方法已经不适合在Delphi中编程了。函数和过程是Pascal 语言的两种子程序,它们可以随时被调用。函数执行时有返回值,而过程执行时,是没有返回值的。函数的调用是为了得到一个返回值,调用过程仅仅为了执行某种操作。函数只能出现在表达式中,不能单独使用;过程不能出现在表达式中,仅能以某个语句的形式出现。
① 函数的使用
函数在调用之前必须先声明。函数声明的语法格式如下:
function 函数名(<形式参数表>):<类型>; begin <语句>; <语句>; ... <语句>; end;
函数调用的语法格式为:
<函数名>(<实参表>);
注意:实参表中的各个参数必须用逗号分割,其顺序必须和函数说明的顺序相对应,并且个数相同、类型相符。
下面举一个函数调用的简单例子,在程序中编写一个名为Total 的自定义函数,主要功能是对传过来的两个参数进行相加计算,返回计算完毕后的结果。主要代码如下:
program FunctionDemo; {$APPTYPE CONSOLE} uses SysUtils; //定义函数,用于计算a、b 参数的和 function Total(a,b:Integer):Integer; begin Total:=a+b; end; var a,b,c:Integer; begin try Writeln('请输入a 值'); Readln(a); Writeln('请输入b 值'); Readln(b); c:=Total(a,b); //调用函数 Write('a 与b 的和为:'); Writeln(c); Readln; except Writeln('输入的内容不是数字'); end; end.
② 过程的使用
过程与函数类似,在调用之前必须进行声明。过程声明的语法格式如下:
proceduce <过程名>(<形式参数表>) begin <语句> <语句> ... <语句> end;
过程调用的语法格式为:
<过程名>(<实参表>)
(6)变体类型(Variant)
变体类型是一种预定义类型,主要是为了支持OLE编程。变体类型能够在运行期间动态地改变类型。变体类型能支持所有简单的数据类型,如整型、浮点型、字符串、布尔型、日期时间、货币及OLE 自动化对象等。
注意:变体类型不能表达Object Pascal 对象。
语法
var myVariant : Variant;
示例
var one,two,three,four,five:variant;//声明5个变体类型变量 begin one:=1; two:='Hello mingri'; three:=true; four:=12.23; five:=one+four; end;
一个 variant 类型的变量要在内存中占用 16 字节,除了要保存值以外,还要用其他的单元保存 Variant 类型的类型码。
当给一个变体类型变量赋予一个数据类型后,编译器将会根据类型自动设定类型码。
表:变体类型码
varEmpty | varNull | varSmallint | varInteger | varSingle | varDouble |
varCurrency | varDate | varOleStr | varDispatch | varError | varBoolean |
varVariant | varUnknown | varByte | varStrArg | varString | varAny |
varTypeMask | varArray | varByRef |
(7)文件类型
文件是指相同类型元素的有序集合。Delphi处理文件有三种方式,一种是使用Object Pascal标准的文件处理技术;一种是使用Windows的文件处理函数;还有一种是使用文件流对象。
Object Pascal标准的文件处理技术,有专门的数据类型和文件处理例程,并且与Windows的标准API不兼容,但对于熟悉Pascal的用户来说,仍然是操作文件的好选择。下面我们就对此进行介绍。
声明一个文件类型的语法如下:
type fileTypeName = file of type
这里,fileTypeName是任何有效的标识符, type是一种大小固定的数据类型,称之为基类型。 基类型不能使用指针类型,不能包括动态数组、长字符串、类、对象、指针、可变类型以及其它文件类型。但可以是其它结构类型,例如:
type PhoneEntry = record
FirstName, LastName: string[20]; PhoneNumber: string[15]; Listed: Boolean; end; PhoneList = file of PhoneEntry;
这里,PhoneEntry是自定义的结构类型,PhoneList是以PhoneEntry为基类型的文件类型。在定义了文件类型后,就可以直接定义文件类型的变量了。例如:
var List1: PhoneList;
有时侯,我们也可以使用 file of 基类型的方式直接定义文件类型变量。例如下面的定义与上面的形式有同样的效果:
var List1: file of PhoneEntry;
如果我们在声明文件类型时不指明其基类型。则这样的文件我们称之为无类型文件,如:
var DataFile: file;
无类型文件主要用于直接访问磁盘文件的多层I/O操作。
示例
procedure buttonClick(Sender: TObject); type myTextFile = TextFile; var TextFile1:MyTextFile; begin Try CreateDir('.\');//将文件定位到当前目录 AssignFile(TextFile1, 'TextFile1.txt');//AssignFile 用于将文件变量与一个文件关联起来 Edit1.Text := GetCurrentDir();//GetCurrenDir 为显示当前文件目录 Rewrite(TextFile1);//创建并打开文件 Writeln(TextFile1, Memo1.Lines.Text);//对文件进行写入操作 CloseFile(TextFile1);//关闭文件 ShowMessage('对文件操作成功!'); Except ShowMessage('对文件操作失败!'); end; end;
(8)日期类型
日期类型属于浮点数据类型。日期范围充1899-12-30~9999-12-31,带负号的值表示1899以前的日期。
语法
var
DateTime1: TDateTime;
在Object Pascal 中,所有的变量必须是某种特定的数据类型,数据类型决定了它所能包含的数值和可进行的操作。Object Pascal 提供了丰富的预定义数据类型,更重要的是它允许定义自己的类型,这就要用到类型声明语句了。
类型也有作用域问题,在程序的 Type 区声明的类型可以在整个程序中使用。在某个过程或函数中声明的类型则只能在这个过程或函数中使用。
类型声明语句的格式如下:
type 类型标识符 = 类型描述;
变量和常量的区别在于变量在运行期间其值是可以改变的。变量必须明确地指定一个数据类型,而常量是通过等号右边的数值隐含确定的。
变量的类型可以是标准的类型,也可以是自己定义的类型,其语法如下:
var 变量标识符:类型;
常量的特点是在它运行期间不能被修改,因此对常量的赋值是非法的。Object Pascal 提供了几个预定义的常量,如True 和False 等,当然也可以声明自己的常量,其语法如下:
Const 常量标识符 = 常数;