unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} //如果要记录一个人的姓名和年龄, 可以: procedure TForm1.Button1Click(Sender: TObject); var name: string; age: Word; begin {赋值} name := '张三'; age := 18; {读取} ShowMessage(Format('%s今年%d岁', [name,age])); {张三今年18岁} end; //不如定义一个结构类型 procedure TForm1.Button2Click(Sender: TObject); type TPerson = record name: string[12]; {在结构里面不能使用长字符串} age: Word; end; var person: TPerson; {声明结构变量} const str = '%s今年%d岁'; {为格式化输出准备一个常量} begin {赋值} person.name := '李四'; person.age := 81; {读取} ShowMessage(Format(str, [person.name,person.age])); {李四今年81岁} end; end.
type TRec1 = record i: Integer; w: Word; end; TRec2 = packed record {压缩结构: 牺牲效率, 减小尺寸} i: Integer; w: Word; end; procedure TForm1.Button1Click(Sender: TObject); begin ShowMessage(IntToStr(SizeOf(TRec1))); {8} ShowMessage(IntToStr(SizeOf(TRec2))); {6} end;
学习结构[记录]类型(3) - 结构间的关系
type
TRec1 = record
name: string[ 12];
age: Word;
end;
TRec2 = record
name: string[ 12];
age: Word;
end;
var
RecA,RecB: TRec1;
RecX,RecY: TRec2;
procedure TForm1 .Button1Click(Sender: TObject);
begin
{给 RecA 赋值}
RecA .name := '张三';
RecA .age := 18;
{复制 RecA 给 RecB}
RecB := RecA;
{现在 RecB 的值和 RecA 一样}
ShowMessage(RecB .name); {张三}
{修改 RecB 的值}
RecB .name := '李四';
{RecA 的值不变, 因为现在 RecA、RecB 是两个不同的数据}
ShowMessage(RecA .name); {张三}
//RecX := RecA; {这样会出错!}
{尽管 RecX 与 RecA 的内部结构完全一样, 但 Delphi 认为它们是不同的数据类型}
RecX := TRec2(RecA); {但可以强制转换}
ShowMessage(RecX .name); {张三}
RecY .name := RecB .name; {交换内部值是可以的}
RecY .age := RecA .age;
ShowMessage(RecY .name); {李四}
end;
TRec1 = record
name: string[ 12];
age: Word;
end;
TRec2 = record
name: string[ 12];
age: Word;
end;
var
RecA,RecB: TRec1;
RecX,RecY: TRec2;
procedure TForm1 .Button1Click(Sender: TObject);
begin
{给 RecA 赋值}
RecA .name := '张三';
RecA .age := 18;
{复制 RecA 给 RecB}
RecB := RecA;
{现在 RecB 的值和 RecA 一样}
ShowMessage(RecB .name); {张三}
{修改 RecB 的值}
RecB .name := '李四';
{RecA 的值不变, 因为现在 RecA、RecB 是两个不同的数据}
ShowMessage(RecA .name); {张三}
//RecX := RecA; {这样会出错!}
{尽管 RecX 与 RecA 的内部结构完全一样, 但 Delphi 认为它们是不同的数据类型}
RecX := TRec2(RecA); {但可以强制转换}
ShowMessage(RecX .name); {张三}
RecY .name := RecB .name; {交换内部值是可以的}
RecY .age := RecA .age;
ShowMessage(RecY .name); {李四}
end;
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; Button4: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} type TRec = record {定义结构 TRec} name: string[12]; age: Word; end; TPRec = ^TRec; {定义 TRec 结构的指针类型 TPRec} var Rec: TRec; {声明结构变量} PRec1,PRec2: TPRec; {声明 TPRec 指针变量} PRec3: ^TRec; {声明 TRec 结构的指针变量, 现在 PRec1 与 PRec3 是不同类型的变量} P: Pointer; {声明无类型指针} //通过结构指针访问结构 procedure TForm1.Button1Click(Sender: TObject); begin Rec.name := '张三'; Rec.age := 18; PRec1 := @Rec; {把 Rec 的地址告诉 PRec1} {本来应该这样访问} ShowMessage(PRec1^.name); {张三} {Delphi 允许这样简单使用结构指针} ShowMessage(PRec1.name); {张三} {如果我们通过指针修改了数据} PRec1.name := '李四'; {那么} ShowMessage(Rec.name); {李四} {因为 PRec1 和 Rec 所指的是同一个数据} end; //如果是单独使用指针, 必须先给内存 procedure TForm1.Button2Click(Sender: TObject); begin GetMem(PRec2, SizeOf(TRec)); PRec2.name := '王五'; PRec2.age := 9; ShowMessage(PRec2.name); {王五} {手工给的内存, 必须手工释放} FreeMem(PRec2); end; //虽是同一个结构的指针, 但还不是一个类型 procedure TForm1.Button3Click(Sender: TObject); begin Rec.name := '孙六'; Rec.age := 16; {把 Rec 的地址给属于 ^Rec 类型的 PRec3 指针} PRec3 := @Rec; ShowMessage(PRec3.name); {孙六} {如果要把 PRec3 知道的值告诉 PRec1, 需要类型转换} PRec1 := TPRec(PRec3); ShowMessage(PRec1.name); {孙六} {反过来想, 如果要把 PRec1 知道的值告诉 PRec3 呢?} Rec.name := '赵七'; Rec.age := 24; PRec1 := @Rec; ShowMessage(PRec1.name); {赵七} {这样转换} TPRec(PRec3) := PRec1; ShowMessage(PRec3.name); {赵七} end; //用无类型指针读写 TRec 中的数据 procedure TForm1.Button4Click(Sender: TObject); begin Rec.name := '杜八'; Rec.age := 36; P := @Rec; {赋值} TPRec(P).name := '侯九'; {取值} ShowMessage(TPRec(P).name); {侯九} end; end.
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} type TRec = record {定义结构 TRec} name: string[12]; age: Word; function RecShow: string; {在结构中只能使用静态方法} end; { TRec 结构方法的实现} function TRec.RecShow: string; const str = '%s今年%d岁了'; begin Result := Format(str, [name,age]); end; //使用结构 procedure TForm1.Button1Click(Sender: TObject); var rec: TRec; begin rec.name := '张三'; rec.age := 18; ShowMessage(rec.RecShow); end; end.
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} type TStr12 = string[12]; {属性不接受 string[12], 只好定义成一个类型或使用 ShortString} TRec = record {定义结构 TRec} strict private {如果不使用 strict 修饰符, 在同一单元内的私有变量也是可以访问的} Fname: TStr12; Fage: Word; procedure Setage(const Value: Word); procedure Setname(const Value: TStr12); public property name: TStr12 read Fname write Setname; property age: Word read Fage write Setage; end; { TRec 属性要调用的方法实现} procedure TRec.Setname(const Value: TStr12); begin if Value='' then {这时可以提出一些条件, 譬如譬如姓名不能为空} Fname := '无名氏' else Fname := Value; end; procedure TRec.Setage(const Value: Word); begin if (Value<150) and (Value>=0) then {这时可以提出一些条件, 譬如年龄一般不会超过 150} Fage := Value else Fage := 0; end; //使用结构 procedure TForm1.Button1Click(Sender: TObject); var rec: TRec; begin rec.name := '张三'; {现在的 name 和 age 已经是属性了} rec.age := 800; ShowMessage(rec.name); {张三} ShowMessage(IntToStr(rec.age)); {0} rec.name := ''; rec.age := 18; ShowMessage(rec.name); {无名氏} ShowMessage(IntToStr(rec.age)); {18} end; end.
结构的方法、属性都是在 Delphi 7 以后的版本中加入的, 非常类似与"类", 但又不如在"类"里完善; 它的其他一些新特性也好像是从"类"里搬过来的, 等在"类"里面研究吧.
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} type TRec = record {定义结构 TRec} name: ShortString; age: Word; constructor Create(str: ShortString; w: Word); {构造函数} end; { TRec 构造函数实现} constructor TRec.Create(str: ShortString; w: Word); begin name := str; age := w; end; //使用结构 procedure TForm1.Button1Click(Sender: TObject); var rec: TRec; begin rec.Create('李四', 81); ShowMessage(rec.name); {李四} end; end.
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls; type TForm1 = class(TForm) Button1: TButton; Button2: TButton; Button3: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); end; var Form1: TForm1; implementation {$R *.dfm} //间接嵌套 procedure TForm1.Button1Click(Sender: TObject); type TWife = record {妻子的数据结构} name: string; age: Word; end; TMan = record {男人的数据结构} name: string; age: Word; wife: TWife; {这里嵌套了结构} end; var man: TMan; begin man.name := '张三'; man.age := 63; man.wife.name := '马翠'; man.wife.age := 36; ShowMessage(man.name + #32 + man.wife.name); {张三 马翠} ShowMessage(IntToStr(SizeOf(man))); end; //直接嵌套 procedure TForm1.Button2Click(Sender: TObject); type TMan = record {男人的数据结构} name: string; age: Word; wife: record name: string; age: Word; end; {直接嵌套结构} end; var man: TMan; begin man.name := '张三'; man.age := 63; man.wife.name := '马翠'; man.wife.age := 36; ShowMessage(man.name + #32 + man.wife.name); {张三 马翠} end; //另外, 说到直接嵌套, 其实结构事先不定义, 也是可以直接使用的, 如: procedure TForm1.Button3Click(Sender: TObject); var man: record name: string; age: Word; end; begin man.name := '李四'; man.age := 81; ShowMessage(man.name); {李四} end; { 注意没有, 在本页的例子中, 结构中的 name 被声明为 string ! 结构中不是不允许长字符串吗? 其实 Delphi 在这里是把 string 当作 ShortString 用的. 也就是说这里的 string 最大容量是 255 个字符, 127 个汉字. } end.
变体结构也就是变体记录, 是一个比较复杂的概念. 专家不提倡使用. 兴趣所致, 我想把它弄明白. 一个最大的无符号整数(Cardinal)是 4294967295, 它的大小是 4 字节, 它的二进制表示是: 11111111 11111111 11111111 11111111 它的低字节的值是 11111111, 也就是十进制的 255
//测试: var c: Cardinal; begin c := 4294967295; ShowMessage(IntToStr(Lo(c))); {会显示: 255; Lo 是获取低字节值的函数} end;
一个 Byte 类型的最大值是 255, 它的大小是 1 个字节, 用二进制表示是:
11111111
假如把一个 Cardinal 类型的值赋给一个 Byte 类型的值, Byte 将只取 Cardinal 的最低字节.
//测试: var c: Cardinal; b: Byte; begin c := 4294967295; b := c; ShowMessage(IntToStr(b)); {255} c := 258; {二进制表示: 00000000 00000000 00000001 00000010} b := c; {b 将只获取: 00000010} ShowMessage(IntToStr(b)); {2} end;
这是我们能否会想到, 在结构会储存时, 一个可以储存 Cardinal 的空间, 当然也能得放下一个 Byte 值;
如果这个值非此即彼, 我们完全不需要两个储存空间.
我猜测, 这应该是 Delphi 设计变体记录的初衷.
//假如有这样一个员工登记表 type TpersonRec = record ID: Integer; {员工编号} case Boolean of {根据分类} True: (A: Cardinal); {如果是股东, 登记年薪} False: (B: Word); {如果不是, 登记日薪} end; var personRec: TpersonRec; begin {先算一算这个结构的大小: ID 是 Integer 类型, 应该是 4 字节大小; A 是 Cardinal 类型, 也应该是 4 字节大小; B 是 Word 类型, 应该是 2 字节大小; 合计为 10 个字节. } {可事实, TpersonRec 只有 8 个字节} ShowMessage(IntToStr(SizeOf(TpersonRec))); {8} { 原因是: 字段 A 和 字段 B 公用了一个储存空间; 当然这个储存空间得依着大的, 是 Cardinal 的尺寸 4 个字节. } //赋值测试: personRec.ID := 110; personRec.A := 100000; {一看就知道是个股东} //取值: ShowMessage(IntToStr(personRec.A)); {100000; 这不可能有错, 十万大洋} //但是: ShowMessage(IntToStr(personRec.B)); {34464 ?! 难道这是工人的日薪吗?} { 首先, A 和 B 两个字段占用同一个空间, 给其中一个赋值, 另一个当然也就有值了; 但因为数据类型的容量不同, 它们的值有可能是不一样的. 在很多情况下, 我们可能根本不去理会另一个值, 但如果的确需要呢? 看下一个例子: } end; type TpersonRec = record ID: Integer; case tag: Boolean of {在这里加了一个 tag 变量} True: (A: Cardinal); False: (B: Word); end; var personRec: TpersonRec; begin {我们可以用 tag 变量来区分, 记录中变体部分的值到底是谁的, 譬如:} personRec.ID := 110; personRec.tag := True; personRec.A := 100000; {股东的的年薪} personRec.ID := 111; personRec.tag := False; personRec.B := 100; {工人的日薪} end; //最经典的变体结构莫过于 Delphi 定义的 TMessage 结构了, 两组数据分分合合都是一体, 多么巧妙啊! TMessage = packed record Msg: Cardinal; case Integer of 0: ( WParam: Longint; LParam: Longint; Result: Longint); 1: ( WParamLo: Word; WParamHi: Word; LParamLo: Word; LParamHi: Word; ResultLo: Word; ResultHi: Word); end;