While simple data structures such as
arrays or sets consist of elements all of the same type, a record can consist of a number of elements of different types, and can take on a huge complexity.
Each separate part of a record is referred to as a field.
record is a reserved word.
type TMember = record firstname, surname : string; address: array [1..3] of string; phone: string; birthdate: TDateTime; paidCurrentSubscription: boolean end;
And even more complex structures are possible, e.g.:
type TMaritalState = (unmarried, married, widowed, divorced); TPerson = record // CONSTANT PART // of course records may be nested name: record first, middle, last: string; end; sex: (male, female); // date of birth dob: TDateTime; // VARIABLE PART case maritalState: TMaritalState of unmarried: ( ); married, widowed: (marriageDate: TDateTime); divorced: (marriageDateDivorced, divorceDate: TDateTime; isFirstDivorce: boolean) end;
Note that fields of the variable part have to be in parentheses. You cannot use the same identifier multiple times, so a slightly different name has to be used for the
marriageDate in case of divorced.
The variable part shares the same memory. So
marriageDateDivorced will have the same value regardless of
maritalState. This behaviour is particularly practical as the following example shows:
type TSpecialWord = record case Byte of 0: (Word_value: Word); // 7 1: (Byte_low, Byte_high: Byte); // 7, 0 2: (Bits: bitpacked array [0..15] of 0..1); // 1, 1, 1, 0, 0, ... end;
This record has only a variable part and enables access to the value of the word, the individual bytes and even to the bits. An identifier is not necessarily required in the
case clause, so this does not occupy any memory. The size of this record is two bytes. In the case of
Bits, this is only possible if
bitpacked is used. Note the order of the bytes, with the less significant byte (LSB) coming first.
Individual fields are accessed by placing a dot between the record name and the field name thus:
a.firstname := 'George'; a.surname := 'Petersen'; a.phone := '789534'; a.paidCurrentSubscription := true;
Alternatively, the whole series of fields can be made available together using the
with a do begin firstname := 'George'; surname := 'Petersen'; phone := '789534'; paidCurrentSubscription := true end;
A record is treated by the program as a single entity, and for example a whole record can be copied (provided the copy is of the same type) thus:
var a, b: TMember; (* main program *) begin // ... assign values to the fields in record a b := a // Now b holds a _copy_ of a. // Do not get confused with references: // a and b still point to _different_ _entities_ of TMember. end.
type // record definition TSpecialDay = record dayName: string; month: integer; day: integer; end; const // TSpecialDay constant christmasDay: TSpecialDay = ( dayName: 'Christmas Day'; month: 12; day: 25; ); // since FPC 3.2.0 you may define an constant array of records like this const SpecialDays: array of TSpecialDay = ( ( dayName: 'Christmas Day' ; month: 12; day: 25), ( dayName: 'New Year''s Day'; month: 1; day: 1) );
Records compared to other structured types
|Encapsulation (combining data and methods + hiding visibility)||No||Yes||Yes||Yes|
|Class constructor and destructor||No||Yes||Yes||Yes|
|Polymorphism (virtual methods)||No||No||Yes||Yes|
|Memory allocation||Stack||Stack||Stack||Heap (Only)|
||Managed Types only||Managed Types only||Managed Types only||All fields|
||all fields zeros||all fields zeros||all fields zeros||returns nil|
|Operator overload (global)||Yes||Yes||Yes||Yes|
|Operator overload (in type only)||No||Yes||No||No|
|Virtual constructors, class reference||No||No||No||Yes|
|Variant part (case) as c/c++ union||Yes||Yes||No||No|
|Bitpacked (really packing)||Yes||Yes||No||No|
Modified from https://forum.lazarus.freepascal.org/index.php/topic,30686.30.html (original author: ASerge).
|simple data types|
|complex data types|