zoukankan      html  css  js  c++  java
  • delphi中的record记录和变体记录 转载 https://blog.csdn.net/yt_maomao/article/details/36631133

    //Integer类型刚好是4个字节,ShortInt类型是1个字节,但是Windows中内存是4字节分配,
    //所以这里其实还是4个字节,用SizeOf可以看到这个record的大小是8字节,这样虽然浪
    //费了空间,但是加快了速度(Windows内存分配中的边界对齐原理)
    TPerson = record
    Age: Integer;
    Sex: ShortInt;
    end;
    TPackedPerson = packed record
    Age: Integer;
    Sex: ShortInt; //使用packed record,用Sizeof可以看到这个record的大小是5字节;
    end;

    TEmployee = record
    ID: Integer; //Integer是4字节
    case Integer of
    0:(YearMoney: Integer); //YearMoney和MonthMoney共用内存,按最大内存分配
    1:(MonthMoney: ShortInt); //该record的大小是8字节
    end;

    TTagEmployee = record
    ID: Integer;
    case Grade: Integer of //这里加入了Grade变量
    0:(YearMoney: Integer); //YearMoney和MonthMoney共用内存,按最大内存分配
    1:(MonthMoney: ShortInt); //该record的大小是12字节(ID+Grade+YearMoney)
    end;

    1.在DELPHI中,我们用record关键字来表明一个记录,有时候,我们还会看到用packed record来声明的记录,这二者的区别就在于存储方式的不同;在windows中,内存的分配一次是4个字节的,而Packed按字节进行内存的申请和分配,这样速度要慢一些,因为需要额外的时间来进行指针的定位。因此如果不用Packed的话,Delphi将按一次4个字节的方式申请内存,因此如果一个变量没有4个字节宽的话也要占4个字节!这样浪费了一些空间,但提高了效率。

    2.变体记录的规则:

           (1)Long  String、WideString、Dynamic  Array、Interface的大小都是指针大小,OleVariant其实就是COM  SDK中的VARIANT结构,大小是16字节。但在Object  Pascal中它们都需要自动终结化,如果它们出现在variant  part中,编译器就无法知道它们是否应该进行终结化――因为不知道当前存储的是哪种类型,因此他们不能出现在变体记录类型中,或者用类似String[10]来定义;

    (2)所有变体字段共享一段内存,而共享内存的大小则由最大变体字段决定—“最长”的变量决定;

    (3)当tag存在时,它也是记录的一个字段。也可以没有tag。 

    (4)记录的变体部分的条件域必须是有序类型——Case后面跟的类型必须是Boolean,Integer等等有序类型。

    (5)记录类型中可以含有变体部分,有点象case语句,但没有最后的end,变体部分必需在记录中其他字段的声明之后。

    3.最经典的变体记录,Delphi中的TMessage结构:

    变体结构也就是变体记录, 是一个比较复杂的概念. 专家不提倡使用.

    一个最大的无符号整数(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;

    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 到最后一个可选的
    分号,)称为变体部分,在变体部分
     tag 是可选的,它可以是任何有效标志符。如果省略了tag,也要省略它后面的冒号(:)。
     ordinalType 表示一种有序类型。
     每个constantList 表示一个ordinalType 类型的常量,或者用逗号隔开的常量序列。在所有的常量
    中,一个值不能出现多次。
     每个Variant 是一个由逗号隔开的、类似于fieldList: type 的声明列表,也就是说,Variant 有下面
    的形式:
    fieldList1: type1;
    ...
    fieldListn: typen;
    这里,每个fieldList 是一个有效标志符,或是由逗号隔开的标志符列表,每个type 表示一种类型,
    最后一个分号是可选的。这些类型不能是长字符串、动态数组、变体类型或接口(都属于动态管
    理类型),也不能是包含上述类型的结构类型,但它们可以是指向这些类型的指针。
    变体记录类型语法复杂,但语义却很简单:记录的变体部分包含几个变体类型,它们共享同一个内存区
    域。你能在任何时候,对任何一个变体类型的任何字段读取或写入,但是,当你改变了一个变体的一个
    字段,又改变了另一个变体的一个字段时,你可能覆盖了自己的数据。如果使用了tag,它就像记录中
    非变体部分一个额外的字段,它的类型是ordinalType。
    变体部分有两个目的。首先,假设你想创建这样一个记录:它的字段有不同类型的数据,但你知道,在
    一个(记录)实例中你永远不需要所有的字段,比如:
    type
    TEmployee = record
    FirstName, LastName: string[40];
    BirthDate: TDate;
    case Salaried: Boolean of
    True: (AnnualSalary: Currency);
    False: (HourlyWage: Currency);
    end;
    这里的想法是,每个雇员或者是年薪,或者是小时工资,但不能两者都有。所以,当你创建一个TEmployee
    的实例时,没必要为每个字段都分配内存。在上面的情形中,变体间的唯一区别在于字段名,但更简单
    的情况是字段拥有不同的类型。看一下更复杂的例子:
    type
    TPerson = record
    FirstName, LastName: string[40];
    BirthDate: TDate;
    case Citizen: Boolean of
    True: (Birthplace: string[40]);
    False: (Country: string[20];
    EntryPort: string[20];
    EntryDate, ExitDate: TDate);
    end;
    type
    TShapeList = (Rectangle, Triangle, Circle, Ellipse, Other);
    TFigure = record
    case TShapeList of
    Rectangle: (Height, Width: Real);
    Triangle: (Side1, Side2, Angle: Real);
    Circle: (Radius: Real);
    Ellipse, Other: ();
    end;
    对每个记录类型的实例,编译器分配足够的内存以容纳最大变体类型的所有字段。可选的tag 和
    constantLists(像上面例子中的Rectangle、Triangle 等)对于编译器管理字段没有任何作用,它们只是为
    了程序员的方便。
    使用变体记录的第二个原因是,你可以把同一个数据当作不同的类型进行处理,即使在编译器不允许类
    型转换的场合。比如,在一个变体类型中,它的第一个字段是64 位实数,在另一个变体类型中,第一个
    字段是32 位整数,你可以把一个值赋给实数(字段),然后再当作整数来读取它的前32 位值(比如,把
    它传给一个需要整数参数的函数)。
    ————————————————
    版权声明:本文为CSDN博主「_毛毛_」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
    原文链接:https://blog.csdn.net/yt_maomao/article/details/36631133

  • 相关阅读:
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    HDU 6143 Killer Names【dp递推】【好题】【思维题】【阅读题】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    POJ 3974 Palindrome【manacher】【模板题】【模板】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6127 Hard challenge【计算机几何】【思维题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 6129 Just do it【杨辉三角】【思维题】【好题】
    HDU 3037 Saving Beans【Lucas定理】【模板题】【模板】【组合数取余】
    8.Math 对象
  • 原文地址:https://www.cnblogs.com/QuincyYi/p/12730132.html
Copyright © 2011-2022 走看看