Delphi记录类型

简介:   学习结构[记录]类型(1) - 开始unit Unit1;interfaceuses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Form...

 

 
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;

 


 
 
 
 
 
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;

 


相关文章
|
6月前
[MFC][bug记录]MFC移植对话框属性
[MFC][bug记录]MFC移植对话框属性
|
C# C++
创建目标类型对象在C#7.3中不可用,请使用9.0或更高的语言版本
创建目标类型对象在C#7.3中不可用,请使用9.0或更高的语言版本
1091 0
创建目标类型对象在C#7.3中不可用,请使用9.0或更高的语言版本
|
7月前
|
存储 SQL 缓存
如何使用 ST05 事物码,快速找到访问指定数据库表的 ABAP 代码试读版
如何使用 ST05 事物码,快速找到访问指定数据库表的 ABAP 代码试读版
60 0
驱动开发:Win10枚举完整SSDT地址表
在前面的博文`《驱动开发:Win10内核枚举SSDT表基址》`中已经教大家如何寻找`SSDT`表基地址了,找到后我们可根据序号获取到指定`SSDT`函数的原始地址,而如果需要输出所有`SSDT`表信息,则可以定义字符串列表,以此循环调用`GetSSDTFunctionAddress()`函数得到,当然在此之间也可以调用系统提供的`MmGetSystemRoutineAddress()`函数顺便把当前地址拿到,并通过循环方式得到完整的SSDT列表。
337 0
驱动开发:Win10枚举完整SSDT地址表
|
Oracle 关系型数据库 安全
(转)用Delphi中的TADOQuery查询Oracle10G中的数据表时,Open时提示"数据类型不被支持"的处理方法
原因是你用的ADO   for   ORACLE的驱动是微软的Microsoft OLE DB provider   for Oracle,不支持BLOB字段, 应使用ORACLE的驱动ORACLE   provider   for   OLE   DB,如果未装Oracle,可以     去Oracle公司网站下载这个驱动。
837 0
|
测试技术 C#
C# /VB.NET 创建PDF项目符号列表和多级编号列表
使用项目符号和编号,可以让文档的层次结构更清晰、更有条理,也更容易突出重点。在编辑文档的过程中,我个人也比较偏爱项目标号来标注文章重点信息。在之前的文章中,介绍了如何在Word中来创建项目标号和编号列表,在本篇文章中,将介绍创建PDF项目符号列表和多级编号列表的方法。
3525 0
|
Java 数据库 数据安全/隐私保护