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-of -|variant- part-item----
                  -identifier- :--               -------;--------

--variant- part- item---constant---:-( ------------)--------------------
                 ----,----|      - field- list-|
____________________________________________

So the following are valid record type declarations:

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

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:

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

Record-typed Constant

When initializing a variable (or constant) of type record, a special syntax must be used: for each field in the record, the name must be specified, followed by a colon and an expression for the field value. The compiler must be able to evaluate the expression at compile time. The values of the field are separated by a semicolon.

_________________________________________________________________________________________________________
Constant record value

                   |--------;---------|
--record- constant- (---identifier-: expression---)------------------------
____________________________________________

For the MyRec record above, the following would initialize all fields:

var
  R : MyRec = (X : 1; Y: 2; Z: 3);

You do not need to specify values for all fields, but then the compiler will warn you. The following is a valid initialization:

  var
    R : MyRec = (X : 1; Y: 2);

But it results in a warning:

cr.pp(11,26) Warning: Some fields coming after "Y" were not initialized

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;
       end;
     {$PackRecords default}

and

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

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.