next up previous contents index search.gif
Next: 3.4 Pointers Up: 3. Types Previous: 3.2 Character types

Subsections


3.3 Structured Types

A structured type is a type that can hold multiple values in one variable. Stuctured types can be nested to unlimited levels.

Structured Types

\begin{syntdiag}\setlength {\sdmidskip}{.5em}\sffamily\sloppy \synt{structured\ ...
...lass\ reference\ type}\\
\synt{set\ type}\\
\synt{file\ type}
\)\end{syntdiag}
Unlike Delphi, Free Pascal does not support the keyword Packed for all structured types, as can be seen in the syntax diagram. It will be mentioned when a type supports the packed keyword. In the following, each of the possible structured types is discussed.

3.3.1 Arrays

Free Pascal supports arrays as in Turbo Pascal, multi-dimensional arrays and packed arrays are also supported:

Array types

\begin{syntdiag}\setlength {\sdmidskip}{.5em}\sffamily\sloppy \synt{array\ type}...
...b] \synt{ordinal\ type} \\ \lit*, \>
\lit*] \lit*{of} \synt{type}\end{syntdiag}
The following is a valid array declaration:

Type
  RealArray = Array [1..100] of Real;
As in Turbo Pascal, if the array component type is in itself an array, it is possible to combine the two arrays into one multi-dimensional array. The following declaration:

Type
   APoints = array[1..100] of Array[1..3] of Real;
is equivalent to the following declaration:

Type
   APoints = array[1..100,1..3] of Real;
The functions High and Low return the high and low bounds of the leftmost index type of the array. In the above case, this would be 100 and 1.

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

\begin{syntdiag}\setlength {\sdmidskip}{.5em}\sffamily\sloppy \synt{record\ type...
...\begin{displaymath}\synt{field\ list} \end{displaymath} \lit*{end}\end{syntdiag}

\begin{syntdiag}\setlength {\sdmidskip}{.5em}\sffamily\sloppy \synt{field\ list}...
...\synt{variant\ part}
\)\begin{displaymath}\lit*; \end{displaymath}\end{syntdiag}

\begin{syntdiag}\setlength {\sdmidskip}{.5em}\sffamily\sloppy \synt{fixed\ fields}
\<[b] \synt{identifier\ list} \lit*: \synt{type} \\ \lit*; \>\end{syntdiag}

\begin{syntdiag}\setlength {\sdmidskip}{.5em}\sffamily\sloppy \synt{variant\ par...
...al\ type\ identifier}
\lit*{of} \<[b] \synt{variant} \\ \lit*; \>\end{syntdiag}

\begin{syntdiag}\setlength {\sdmidskip}{.5em}\sffamily\sloppy \synt{variant} \<[...
...t*( \begin{displaymath}\synt{field\ list} \end{displaymath} \lit*)\end{syntdiag}
So the following are valid record types 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 time. In effect, it introduces a new field in the record. Remark that 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;
The size of a record is the sum of the sizes of its fields, each size of a field is rounded up to two. If the record contains a variant part, the size of the variant part is the size of the biggest variant, plus the size of the tag field type if an identifier was declared for it. Here also, the size of each part is first rounded up to two. So in the above example, SizeOf would return 24 for Point, 24 for RPoint and 26 for BetterRPoint. For MyRec, the value would be 12. If you want to read a typed file with records, produced by a Turbo Pascal program, then chances are that you will not succeed in reading that file correctly. The reason for this is that by default, elements of a record are aligned at 2-byte boundaries, for performance reasons. This default behaviour can be changed with the {$PackRecords n} switch. Possible values for n are 1, 2, 4, 16 or Default. This switch tells the compiler to align elements of a record or object or class that have size larger than n on n byte boundaries. Elements that have size smaller or equal than n are aligned on natural boundaries, i.e. to the first power of two that is larger than or equal to the size of the record element. The keyword Default selects the default value for the platform you're working on (currently, this is 2 on all platforms) Take a look at the following program:

Program PackRecordsDemo;
type
   {$PackRecords 2}
     Trec1 = Record
       A : byte;
       B : Word;
     end;

     {$PackRecords 1}
     Trec2 = Record
       A : Byte;
       B : Word;
       end;
   {$PackRecords 2}
     Trec3 = Record
       A,B : byte;
     end;

    {$PackRecords 1}
     Trec4 = Record
       A,B : Byte;
       end;
   {$PackRecords 4}
     Trec5 = Record
       A : Byte;
       B : Array[1..3] of byte;
       C : byte;
     end;

     {$PackRecords 8}
     Trec6 = Record
       A : Byte;
       B : Array[1..3] of byte;
       C : byte;
       end;
   {$PackRecords 4}
     Trec7 = Record
       A : Byte;
       B : Array[1..7] of byte;
       C : byte;
     end;

     {$PackRecords 8}
     Trec8 = Record
       A : Byte;
       B : Array[1..7] of byte;
       C : byte;
       end;
Var rec1 : Trec1;
    rec2 : Trec2;
    rec3 : TRec3;
    rec4 : TRec4;
    rec5 : Trec5;
    rec6 : TRec6;
    rec7 : TRec7;
    rec8 : TRec8;

begin
  Write ('Size Trec1 : ',SizeOf(Trec1));
  Writeln (' Offset B : ',Longint(@rec1.B)-Longint(@rec1));
  Write ('Size Trec2 : ',SizeOf(Trec2));
  Writeln (' Offset B : ',Longint(@rec2.B)-Longint(@rec2));
  Write ('Size Trec3 : ',SizeOf(Trec3));
  Writeln (' Offset B : ',Longint(@rec3.B)-Longint(@rec3));
  Write ('Size Trec4 : ',SizeOf(Trec4));
  Writeln (' Offset B : ',Longint(@rec4.B)-Longint(@rec4));
  Write ('Size Trec5 : ',SizeOf(Trec5));
  Writeln (' Offset B : ',Longint(@rec5.B)-Longint(@rec5),
           ' Offset C : ',Longint(@rec5.C)-Longint(@rec5));
  Write ('Size Trec6 : ',SizeOf(Trec6));
  Writeln (' Offset B : ',Longint(@rec6.B)-Longint(@rec6),
           ' Offset C : ',Longint(@rec6.C)-Longint(@rec6));
  Write ('Size Trec7 : ',SizeOf(Trec7));
  Writeln (' Offset B : ',Longint(@rec7.B)-Longint(@rec7),
           ' Offset C : ',Longint(@rec7.C)-Longint(@rec7));
  Write ('Size Trec8 : ',SizeOf(Trec8));
  Writeln (' Offset B : ',Longint(@rec8.B)-Longint(@rec8),
           ' Offset C : ',Longint(@rec8.C)-Longint(@rec8));
end.
The output of this program will be :

Size Trec1 : 4 Offset B : 2
Size Trec2 : 3 Offset B : 1
Size Trec3 : 2 Offset B : 1
Size Trec4 : 2 Offset B : 1
Size Trec5 : 8 Offset B : 4 Offset C : 7
Size Trec6 : 8 Offset B : 4 Offset C : 7
Size Trec7 : 12 Offset B : 4 Offset C : 11
Size Trec8 : 16 Offset B : 8 Offset C : 15
And this is as expected. In Trec1, since B has size 2, it is aligned on a 2 byte boundary, thus leaving an empty byte between A and B, and making the total size 4. In Trec2, B is aligned on a 1-byte boundary, right after A, hence, the total size of the record is 3. For Trec3, the sizes of A,B are 1, and hence they are aligned on 1 byte boundaries. The same is true for Trec4. For Trec5, since the size of B - 3 - is smaller than 4, B will be on a 4-byte boundary, as this is the first power of two that is larger than it's size. The same holds for Trec6. For Trec7, B is aligned on a 4 byte boundary, since it's size - 7 - is larger than 4. However, in Trec8, it is aligned on a 8-byte boundary, since 8 is the first power of two that is greater than 7, thus making the total size of the record 16. As from version 0.9.3, Free Pascal supports also the 'packed record', this is a record where all the elements are byte-aligned. Thus the two following declarations are equivalent:

     {$PackRecords 1}
     Trec2 = Record
       A : Byte;
       B : Word;
       end;
     {$PackRecords 2}
and

     Trec2 = Packed Record
       A : Byte;
       B : Word;
       end;
Note the {$PackRecords 2} after the first declaration !

3.3.3 Set types

Free Pascal supports the set types as in Turbo Pascal. The prototype of a set declaration is:

Set Types

\begin{syntdiag}\setlength {\sdmidskip}{.5em}\sffamily\sloppy \synt{set\ type} \lit*{set} \lit*{of} \synt{ordinal\ type}\end{syntdiag}
Each of the elements of SetType must be of type TargetType. TargetType can be any ordinal type with a range between 0 and 255. A set can contain maximally 255 elements. The following are valid set declaration:

Type
    Junk = Set of Char;

    Days = (Mon, Tue, Wed, Thu, Fri, Sat, Sun);
    WorkDays : Set of days;
Given this set declarations, the following assignment is legal:

WorkDays := [ Mon, Tue, Wed, Thu, Fri];
The operators and functions for manipulations of sets are listed in table (SetOps) .

Table: Set Manipulation operators
Operation Operator
Union +
Difference -
Intersection *
Add element include
Delete element exclude

You can compare two sets with the <> and = operators, but not (yet) with the < and > operators. As of compiler version 0.9.5, the compiler stores small sets (less than 32 elements) in a Longint, if the type range allows it. This allows for faster processing and decreases program size. Otherwise, sets are stored in 32 bytes.

3.3.4 File types

File types are types that store a sequence of some base type, which can be any type except another file type. It can contain (in principle) an infinite number of elements. File types are used commonly to store data on disk. Nothing stops you, however, from writing a file driver that stores it's data in memory. Here is the type declaration for a file type:

File types

\begin{syntdiag}\setlength {\sdmidskip}{.5em}\sffamily\sloppy \synt{file\ type} ...
...*{file} \begin{displaymath}\lit*{of} \synt{type} \end{displaymath}\end{syntdiag}
If no type identifier is given, then the file is an untyped file; it can be considered as equivalent to a file of bytes. Untyped files require special commands to act on them (see Blockread, Blockwrite). The following declaration declares a file of records:

Type
   Point = Record
     X,Y,Z : real;
     end;
   PointFile = File of Point;
Internally, files are represented by the FileRec record. See chapter refchapter for it's declaration.

A special file type is the Text file type, represented by the TextRec record. A file of type Text uses special input-output routines.


root
1999-06-10