3.3.2 Record types

Free Pascal supports fixed records and records with variant parts. The syntax diagram for a record type is

Record types

--         --------------     ------------    -------------------
  record type --      --| record  -       -|end
             -bpitapcakecdked -|         field list

--field list ----------fixed fields-------------------------------------
            -        -  -|variant part    ;
             fixed fields ;

--fixed fields-|identifier list :-type----------------------------------
            --------- ;----------

--variant part case--|-------------ordinal type identifier of-|variant----
                  -identifier- :--                       ---;-----

--variant-|-constant- ,---:- (------------)------------------------
         |-----------|       -field list--|

So the following are valid record type declarations:

  Point = Record  
          X,Y,Z : Real;  
  RPoint = Record  
          Case Boolean of  
          False : (X,Y,Z : Real);  
          True : (R,theta,phi : Real);  
  BetterRPoint = Record  
          Case UsePolar : Boolean of  
          False : (X,Y,Z : Real);  
          True : (R,theta,phi : Real);  

The variant part must be last in the record. The optional identifier in the case statement serves to access the tag field value, which otherwise would be invisible to the programmer. It can be used to see which variant is active at a certain time3. In effect, it introduces a new field in the record.

Remark: It is possible to nest variant parts, as in:

  MyRec = Record  
          X : Longint;  
          Case byte of  
            2 : (Y : Longint;  
                 case byte of  
                 3 : (Z : Longint);  

Record layout and size

The layout and size of a record is influenced by five aspects:

The layout and size of variant parts in records is determined by replacing them with a field whose type is a record with as first element a field of the tag field type if an identifier was declared for this tag field, followed by the elements of the biggest variant.

Field F2’s offset in a record is equal to the sum of the previous field F1’s offset and F1’s size, rounded up to a multiple of F2’s required alignment. This required alignment is calculated as follows:

The size of a record is equal to the sum of the record’s last field’s offset and this field’s size, rounded up to a multiple of the record’s required alignment. The record’s required alignment is calculated as follows:

Remarks and examples

Free Pascal also supports a ’packed record’, which is a record where all the elements are byte-aligned. As a result, the two following declarations are equivalent:

     {$PackRecords 1}  
     Trec2 = Record  
       A : Byte;  
       B : Word;  
     {$PackRecords default}


     Trec2 = Packed Record  
       A : Byte;  
       B : Word;  

Note the {$PackRecords Default} after the first declaration to restore the default setting!

Given the platform-dependent nature of how records are laid out in memory, the only way to ensure a compatible layout across platforms (assuming that all fields are declared using a type with the same meaning across these same platforms) is by using {$PACKRECORDS 1}.

In particular, if a typed file with records, produced by a Turbo Pascal program, must be read, then chances are that attempting to read that file correctly will fail. The reason is that Free Pascal’s default {$PACKRECORDS N} setting is not necessarily compatible with Turbo Pascal’s. It can be changed to {$PACKRECORDS 1} or {$PACKRECORDS 2} depending on the setting used in the Turbo Pascal program that create the file (although it may still fail with {$PACKRECORDS 2} due to different type alignment requirements between 16 bit MSDOS and your current platform).

The same remark goes for Delphi: exchanging data is only guaranteed to be possible if both the producer and consumer use a packed record, or if they are on the same platform and use the same {$PACKRECORDS X} setting.

3However, it is up to the programmer to maintain this field.