DELPHI学习---结构类型

DELPHI学习---结构类型

Structured types (结构类型)

结构类型的一个实例可包含多个值。结构类型包括集合、数组、记录,也包括类、类引用(class-reference)
和接口类型。除了集合只能包含有序值以外,结构类型可以包含其它的结构类型,且结构的层次不受限
制。
默认情况下,一个结构类型的值被圆整为字(word)或者双字(double-word),这样访问起来更迅速。
当声明一个结构类型时,可以包含关键字packed,这将对数据的存储进行压缩(并不是压缩,只是不再
圆整数据,而保留它的自然结构)。比如:
type TNumbers = packed array[1..100] of Real;
使用packed 使数据访问变慢,并且在使用字符数组的情况下,能影响类型兼容性。

{*-----------------------------------------------------------------------------------------

Packed关键字是与编译有关的!!!
    在默认情况下,Delphi编译器是对数据进行优化的!!!   
    
    例如:   
    TX     =     Record   
          A     :     Char;   
          B     :     Integer;   
    End;   
    
    在这个记录中,TX的大小应该是5个字节,但是由于Delphi的优化,Tx的大小变成了8个字节,所以为了得到

TX的真实大小,这个时候就要使用Packed关键字了,禁止进行优化!!!一般Packed关键字在使用C++/C的

API汉书中用得比较多,一般Delphi内部自己的程序不需要使用Packed关键字,除非你想得到某个对象的真实大小!!!   
    为什么优化:   
    举一个现实生活中的例子,假设有两个杯子,一个装满了水,另一个只装了1/3的水,那么实际物理上这些水

占用了两个杯子,因为我们不能说占了4/3的杯子,因为我们没办法得到2/3的杯子。在这一杯子就是CPU中的寄

存器,TX对象中的A,虽然只有一个字节,但它却占用了一个寄存器的空间(4个字节,这就是32位计算机,如果

是64位计算机,一个寄存器的大小将变成8个字节),所以经过优化的结果是TX的大小是8。优化的数据可以提高

执行效率,因为我们不必关心杯子里到底有多少水,1/4?1/3?1/2...,我们只要关心那些水占据了一个杯子,

这样会节省大量的分析杯子里有多少水的时间!!!这就是Packed的作用!!!

 

加Packed就是为了   
    1、使用sizeOf得到记录的真实大小。   
    2、节省内存空间。   
    3、保证与其他程序通讯的正确性。   
    ....   
    
    比如你编写一个通讯程序,定义的记录大小为10用来存储现金,如果这时你让Delphi优化

了这个记录,天知道金额会变成什么!!!

packed     record   
    是压缩记录类型

----------------------------------------------------------------------------------------*}

 

Sets(集合) 

集合是同一种有序类型的值的聚集,它们包含的值没有内在的顺序,且一个值在集合中包含两次并没有
实际意义。
一个集合类型的取值范围,是构成它的有序类型(称为基础类型)的幂,也就是说,集合可能的值是基
础类型的所有子集,也包含空集。基础类型可能的值不要超过256 个,并且它们的序数必须在0 到255
之间。任何像下面的形式:
set of baseType
声明一个集合类型,这里,baseType 是一个合适的有序类型。
因为基础类型的值是有限的,因此,集合类型通常使用子界类型来定义。比如下面的声明:
type
TSomeInts = 1..250;
TIntSet = set of TSomeInts;
它声明一个叫做TIntSet 的集合类型,它的值是从1 到250 之间所有可能的选择。你也可以使用下面的语
句达到同样的目的:
type TIntSet = set of 1..250;          //用set of  的形式定义
有了上面的声明,你就可以像下面这样构造集合了:
var Set1, Set2: TIntSet;
...
Set1 := [1, 3, 5, 7, 9];          //集合用[]来赋值
Set2 := [2, 4, 6, 8, 10]
你也可以直接使用set of …构造直接声明变量:
var MySet: set of 'a'..'z';
...
MySet := ['a','b','c'];
其它集合类型的实例包括:
set of Byte
set of (Club, Diamond, Heart, Spade)
set of Char;
运算符in 判断集合的成员关系:
if 'a' in MySet then ... { do something } ;
每个集合类型可包含空集,用[]来表示。

 

Arrays(数组)
一个数组是由相同类型的(称为基础类型)、经过索引的元素组成的聚集。因为每个元素有唯一的索引,
所以,数组和集合不同,它可以包含多个相同的值。数组可以静态分配内存,也可以动态分配。
Static arrays(静态数组)
静态数组类型以下面的格式声明:
array[indexType1, ..., indexTypen] of baseType
这里,每个indexType 是有序类型并且范围不超过2G。因为indexType 是数组的索引,所以,数组包含
的元素个数由indexType 的范围限定。在实际应用中,indexType 通常是整数子界类型。
最简单的是一维数组,它只有一个indexType,比如:
var MyArray: array[1..100] of Char;
声明了一个变量MyArray,它是一个有100 个字符的数组。给定上面的声明,MyArray[3]表示数组中的
第3 个字符。若声明了一个静态数组,虽然并没有给每一个元素赋值,但未用的元素仍分配内存并包含
一个随机值,这和未初始化的变量类似。
A multidimensional array is an array of arrays. For example,
一个多维数组是数组的数组,比如:
type TMatrix = array[1..10] of array[1..50] of Real;
就等价于
type TMatrix = array[1..10, 1..50] of Real;
不论用哪种方式声明,它表示一个有500 个实数值的数组。一个TMatrix 类型的变量MyMatrix,可使用
这样的索引:MyMatrix[2,45],或像这样:MyMatrix[2][45]。同样,
packed array[Boolean,1..10,TShoeSize] of Integer;
就等价于
packed array[Boolean] of packed array[1..10] of packed array[TShoeSize] of
Integer;
标准函数Low 和High 作用于数组类型(的标志符)或变量,它们返回数组第1 个索引(类型)的最小
值和最大值;Length 返回数组第1 维的元素个数。
一维、
一维、压缩的(packed)、Char 类型的静态数组称为packed string,它和字符串类型兼容,也和其它具有
相同元素个数的packed string 兼容。请参考Type compatibility and identity。
array[0..x] of Char 类型的数组,是0 下标开始的字符数组,它用来存储零结尾字符串,并且和PChar 类
型兼容。参考Working with null-terminated strings。


Dynamic arrays(动态数组)
动态数组没有固定大小和长度,相反,当你给它赋值或把它传给SetLength 函数时,它的内存被重新分
配。动态数组以下面的形式声明:
array of baseType
比如
var MyFlexibleArray: array of Real;
声明一个实数类型的一维动态数组。声明并没有为MyFlexibleArray 分配内存,要在内存中创建数组,要
调用SetLength。比如,以上面的声明为例:
SetLength(MyFlexibleArray, 20);
分配一个由20 个实数构成的数组,索引号从0 到19。动态数组的索引总是整数,并从0 开始。动态数
组变量实际是指针,并和长字符串一样使用引用计数进行管理。要取消动态数组的分配,给它的变量赋
值nil,或者把变量传给Finalize。在没有其它引用的情况下,这两种方法都将消除数组。0 长度动态数
组的值为nil。不要对一个动态数组变量使用运算符‘^’,也不要对它使用New 或Dispose 过程。
若X 和Y 是同一类型的动态数组变量,X := Y 使X 指向和Y 相同的数组(在这个操作之前,不必给X
分配内存)。不像字符串和静态数组,动态数组不会在被写之前自动拷贝。比如,在下面的代码执行后
var
A, B: array of Integer;
begin
SetLength(A, 1);
A[0] := 1;
B := A;
B[0] := 2;
end;
A[0]的值是2(若A 和B 是静态数组,A[0]仍然是1)。
使用索引给动态数组赋值(比如,MyFlexibleArray[2] := 7),不会为数组重新分配内存;编译时,索引
边界检查也不会给出提示。
当比较动态数组变量时,是比较它们的引用(这里的值是一个地址),而不是它们的值。所以,下面的代
码执行后
var
A, B: array of Integer;
begin
SetLength(A, 1);
SetLength(B, 1);
A[0] := 2;
B[0] := 2;
end;
A = B 返回False,但A[0] = B[0]返回True。
要截断一个动态数组,把它传给SetLength 或Copy,并把返回的值赋给数组变量(SetLength 通常更快)。
比如,若A 是一个动态数组,执行A := SetLength(A, 0, 20),除A 的前20 个元素外,其它都将被截取掉。
一旦一个动态数组被分配内存,你可以把它传给几个标准函数:Length、High 和Low。Length 返回数组
的元素个数,High 返回最大数组的最大索引(也就是Length-1),Low 返回0。对于长度为0 的数组,
High 返回-1(得到反常结果High < Low)。
注意:有些函数或过程在声明时,数组参数表示为array of baseType,没有指明索引类型。比如,
function CheckStrings(A: array of string): Boolean;
这表明,函数可用于(指定的)基础类型的所有数组,而不管它们的大小和索引,也不管它们是静态分
配还是动态分配。请参考Open array parameters。


Multidimensional dynamic arrays(多维动态数组)
要声明多维动态数组,使用复合array of ... 结构,比如,
type TMessageGrid = array of array of string;
var Msgs: TMessageGrid;
声明一个二维字符串数组。要实例化这个数组,应用两个整数参数调用SetLength。比如,若I 和J 是整
数变量,
SetLength(Msgs,I,J);
给它分配内存,Msgs[0,0]表示它的一个元素。
你也能创建不规则的多维动态数组。第一步是调用SetLength,给它传递参数作为前面的(几个)索引。
比如,
var Ints: array of array of Integer;
SetLength(Ints,10);
为Ints 分配了10 行,但没有分配列。接下来,你能每次分配一个列(给它们指定不同的长度),比如,
SetLength(Ints[2], 5);
使Ints 的第3 行有5 个元素。此时(即使其它列没有被分配),你能给它的第3 行赋值,比如,Ints[2,4] :=
6。
下面的例子使用动态数组(IntToStr 函数在SysUtils 单元声明)来创建一个字符串的三角形矩阵。
var
A : array of array of string;
I, J : Integer;
begin
SetLength(A, 10);
for I := Low(A) to High(A) do
begin
SetLength(A[I], I);
for J := Low(A[I]) to High(A[I]) do
A[I,J] := IntToStr(I) + ',' + IntToStr(J) + ' ';
end;
end;
Array types and assignments(数组类型和赋值)
Arrays are assignment-compatible only if they are of the same type. Because Pascal uses name-equivalence for
types, the following code will not compile.
只有数组是相同类型时,它们才是赋值兼容的。因为Pascal 使用‘名称’代表‘类型’,所以下面的代
码无法编译:
Data types, variables and constants
- 59 -
var
Int1: array[1..10] of Integer;
Int2: array[1..10] of Integer;
...
Int1 := Int2;
要使赋值能够工作,要如下声明变量
var Int1, Int2: array[1..10] of Integer;

type IntArray = array[1..10] of Integer;
var
Int1: IntArray;
Int2: IntArray;

 

Records(记录)
记录(类似于其它语言中的结构)表示不同种类的元素的集合,每个元素称为“字段”,声明记录类型时
要为每个字段指定名称和类型。声明记录的语法是
type recordTypeName = record
fieldList1: type1;
...
fieldListn: typen;
end
这里,recordTypeName 是一个有效标志符,每个type 表示一种类型,每个fieldList 是一个有效标志符或
用逗号隔开的标志符序列,最后的分号是可选的。(哪个分号?是最后一个字段的,还是end 后面的?)
比如,下面的语句声明了一个记录类型TDateRec:
type
TDateRec = record
     Year: Integer;
     Month: (Jan, Feb, Mar, Apr, May, Jun, Jul, Aug, Sep, Oct, Nov, Dec);
     Day: 1..31;
end;
TDateRec 包含3 个字段:一个整数类型的Year,一个枚举类型的Month,和另一个子界类型的Day。标
志符Year、Month 和Day 是TDateRec 的字段,它们的行为就像变量。声明并不会为Year、Month 和Day
分配内存,只有在实例化时才进行分配,像下面的样子:
var Record1, Record2: TDateRec;
上面的变量声明创建了两个TDateRec 实例,分别叫做Record1 和Record2。
你可以用记录名作限定符、通过字段名来访问字段:
Record1.Year := 1904;
Record1.Month := Jun;
Record1.Day := 16;
或使用with 语句:
with Record1 do
begin
Year := 1904;
Month := Jun;
Day := 16;
end;
现在,你可以把Record1 的值拷贝给Record2:
Record2 := Record1;
因为字段名的范围被限定在记录本身,你不必担心字段名和其它变量发生冲突。
Instead of defining record types, you can use the record ... construction directly in variable declarations:
除了定义记录类型,你也可以使用record ...构造直接声明变量:
var S: record
Name: string;
Age: Integer;
end;
但是,这样不能让你重复使用类型声明,并且,这样声明的类型不是赋值兼容的,即使它们(记录)的
结构完全相同。


Variant parts in records(记录中的变体部分,变体记录)
一个记录类型能拥有变体部分,它看起来就像case 语句,在声明中,变体部分必须跟在其它字段的后面。
要声明一个变体记录,使用下面的语法:
type recordTypeName = record
fieldList1: type1;
...
fieldListn: typen;
case tag: ordinalType of
constantList1: (Variant1);
...
constantListn: (Variantn);
end;
声明的前面部分(直到关键字case)和标准记录类型一样,声明的其余部分(从case 到最后一个可选的
分号,)称为变体部分,在变体部分

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值