3.3.1 Arrays

Free Pascal supports arrays as in Turbo Pascal. Multi-dimensional arrays and (bit)packed arrays are also supported, as well as the dynamic arrays of Delphi:

_________________________________________________________________________________________________________
Array types

--array- type--|-----------array--|-------------------- of -type------
            --packed --|       -[---ordinal- type--] --
             bitpacked                   ,
____________________________________________

Static arrays

When the range of the array is included in the array definition, it is called a static array. Trying to access an element with an index that is outside the declared range will generate a run-time error (if range checking is on). The following is an example of a valid array declaration:

Type
  RealArray = Array [1..100] of Real;

Valid indexes for accessing an element of the array are between 1 and 100, where the borders 1 and 100 are included. 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 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. You should use them whenever possible, since it improves maintainability of your code. The use of both functions is just as efficient as using constants, because they are evaluated at compile time.

When static array-type variables are assigned to each other, the contents of the whole array is copied. This is also true for multi-dimensional arrays:

program testarray1;

Type
  TA = Array[0..9,0..9] of Integer;

var
  A,B : TA;
  I,J : Integer;
begin
  For I:=0 to 9 do
    For J:=0 to 9 do
      A[I,J]:=I*J;
  For I:=0 to 9 do
    begin
    For J:=0 to 9 do
      Write(A[I,J]:2,' ');
    Writeln;
    end;
  B:=A;
  Writeln;
  For I:=0 to 9 do
    For J:=0 to 9 do
      A[9-I,9-J]:=I*J;
  For I:=0 to 9 do
    begin
    For J:=0 to 9 do
      Write(B[I,J]:2,' ');
    Writeln;
    end;
end.

The output of this program will be two identical matrices.

Dynamic arrays

As of version 1.1, Free Pascal also knows dynamic arrays: In that case the array range is omitted, as in the following example:

Type
  TByteArray = Array of Byte;

When declaring a variable of a dynamic array type, the initial length of the array is zero. The actual length of the array must be set with the standard SetLength function, which will allocate the necessary memory to contain the array elements on the heap.

The following example will set the length to 1000:

Var
  A : TByteArray;

begin
  SetLength(A,1000);

After a call to SetLength, valid array indexes are 0 to 999: the array index is always zero-based.

SetLength can also be used for multi-dimensional arrays. The following example will create a “rectangular” array:

Var
  A : Array of TByteArray;

begin
  SetLength(A,10,100);

After a call to SetLength, valid array indexes are 0 to 9 for the first dimension, and 0 to 99 for the second dimension.

In difference with static multi-dimensional arrays, dynamic arrays do not need to be “rectangular”, i. e. the various elements can have different lengths:

var
  a: array of array of array of LongInt;
  i, j, k: LongInt;
begin
  SetLength(a, 10, 5);
  SetLength(a[5], 3);

  for i := Low(a) to High(a) do
    for j := Low(a[i]) to High(a[i]) do begin
      SetLength(a[i, j], i * 10 + j);
      for k := Low(a[i, j]) to High(a[i, j]) do
        a[i, j, k] := i * 10000 + j * 100 + k;
    end;

  for i := Low(a) to High(a) do begin
    for j := Low(a[i]) to High(a[i]) do begin
      for k := Low(a[i, j]) to High(a[i, j]) do
        Writeln(a[i, j, k]);
      Writeln('-------');
    end;
    Writeln('=======');
  end;
end.

Note that the length of the array is set in elements, not in bytes of allocated memory (although these may be the same). The amount of memory allocated is the size of the array multiplied by the size of 1 element in the array. The memory will be disposed of at the exit of the current procedure or function.

It is also possible to resize the array: in that case, as much of the elements in the array as will fit in the new size, will be kept. The array can be resized to zero, which effectively resets the variable.

At all times, trying to access an element of the array with an index that is not in the current length of the array will generate a run-time error.

Dynamic arrays are reference counted: assignment of one dynamic array-type variable to another will let both variables point to the same array. Contrary to ansistrings, an assignment to an element of one array will be reflected in the other: there is no copy-on-write. Consider the following example:

Var
  A,B : TByteArray;

begin
  SetLength(A,10);
  A[0]:=33;
  B:=A;
  A[0]:=31;

After the second assignment, the first element in B will also contain 31.

It can also be seen from the output of the following example:

program testarray1;

Type
  TA = Array of array of Integer;

var
  A,B : TA;
  I,J : Integer;
begin
  Setlength(A,10,10);
  For I:=0 to 9 do
    For J:=0 to 9 do
      A[I,J]:=I*J;
  For I:=0 to 9 do
    begin
    For J:=0 to 9 do
      Write(A[I,J]:2,' ');
    Writeln;
    end;
  B:=A;
  Writeln;
  For I:=0 to 9 do
    For J:=0 to 9 do
      A[9-I,9-J]:=I*J;
  For I:=0 to 9 do
    begin
    For J:=0 to 9 do
      Write(B[I,J]:2,' ');
    Writeln;
    end;
end.

The output of this program will be a matrix of numbers, and then the same matrix, mirrored.

As remarked earlier, dynamic arrays are reference counted: if in one of the previous examples A goes out of scope and B does not, then the array is not yet disposed of: the reference count of A (and B) is decreased with 1. As soon as the reference count reaches zero the memory, allocated for the contents of the array, is disposed of.

The SetLength call will make sure the reference count of the returned array is 1, that is, if two dynamic array variables were pointing to the same memory they will no longer do so after the setlength call:

program testunique;

Type
  TA = array of Integer;

var
  A,B : TA;
  I : Integer;

begin
  Setlength(A,10);
  For I:=0 to 9 do
    A[I]:=I;
  B:=A;
  SetLength(B,6);
  A[0]:=123;
  For I:=0 to 5 do
    Writeln(B[I]);
end.

It is also possible to copy and/or resize the array with the standard Copy function, which acts as the copy function for strings:

program testarray3;

Type
  TA = array of Integer;

var
  A,B : TA;
  I : Integer;

begin
  Setlength(A,10);
  For I:=0 to 9 do
      A[I]:=I;
  B:=Copy(A,3,6);
  For I:=0 to 5 do
    Writeln(B[I]);
end.

The Copy function will copy six elements of the array to a new array. Starting at the element at index 3 (i. e. the fourth element) of the array.

The Length function will return the number of elements in the array. The Low function on a dynamic array will always return 0, and the High function will return the value Length-1, i. e., the value of the highest allowed array index.

Array constants

For typed constants or initialized variables that have as type a static or dynamic array, a constant value must be specified. A constant array value is specified using round brackets ( and ), with the elements in between:

_________________________________________________________________________________________________________
Constant array value

                  -----,----|
--array- constant- (---expression ---)----------------------------------
____________________________________________

As an example:

uses sysutils;

var
  A : TBytes = (0,1,2);
  B : Array[1..3] of byte = (0,1,2);

begin
end.

For dynamic arrays you can also use an array constructor in code:

var
  A : TBytes;

begin
  A:=TBytes.Create(0,1,2);
end.

The array constructor cannot be used in an initialized constant.

The constant value for an array cannot be used in an expression, only in a constant or variable declaration. So the following will not work:

var
  A : TBytes;

begin
  A:=(0,1,2);
end.

Dynamic array Type compatibility

Object Pascal is a strictly typed language. Two technically distinct types are sometimes considered assignment compatible (i. e. a value of one type can be assigned to a variable of another type) under certain circumstances. Dynamic arrays are considered assignment compatible when they use the same element type. That means that the following will compile:

{$mode objfpc}

Type
  TA = Array of Integer;
  TB = Array of Integer;

Var
  A : TA;
  B : TB;

begin
  SetLength(A,1);
  A[0]:=1;
  B:=A;
end.

But the following will not, even though the integer and word types are assignment compatible:

{$mode objfpc}

Type
  TA = Array of Word;
  TB = Array of Integer;

Var
  A : TA;
  B : TB;

begin
  SetLength(A,1);
  A[0]:=1;
  B:=A;
end.

Note that you can always typecast a dynamic array to a pointer.

type
  TIntArr = array of LongInt;
  PLongInt = ^LongInt;

var
  xs: TIntArr;
  p: PLongInt;
begin
  setlength(xs, 3);
  xs[0] := 69;
  xs[1] := 10;
  xs[2] := 5;
  p := PLongInt(xs); // <-- cast dynamic array to pointer
  writeln(xs[0], xs[1], xs[2]); // 69105
  writeln(p^, p[1], p[2]); // 69105
end.

The opposite typecast is also allowed by the compiler, but must not be used since the automatic referencing and length determination will not work correctly. The following program will print ’3’ for the length of the byte array, which is of course wrong:

type
  TIntArr = array of LongInt;
  PLongInt = ^LongInt;
  TByteArr = Array of Byte;

var
  xs: TIntArr;
  p: PLongInt;
begin
  setlength(xs, 3);
  xs[0] := 69;
  xs[1] := 10;
  xs[2] := 5;
  p := PLongInt(xs);
  Writeln(Length(TByteArr(P))); // Cast to dynamic array
end.

Dynamic array constructor

As of version 3.0 of Free Pascal, Dynamic array types have a constructor. This is intrinsic, the compiler provides it. Up to version 2.6.4, the only way to initialize a dynamic array was as follows:

Type
  TIntegerArray = Array of Integer;

var
  A : TIntegerArray;

begin
  SetLength(A,3);
  A[0]:=1;
  A[1]:=2;
  A[3]:=3;
  Writeln(Length(A));
end.

As of version 3.0 of Free Pascal, an dynamic array can be initialized using a constructor-like syntax. The constructor is called Create, and accepts as parameters a variable number of parameters of the element type of the array type. This means the above initialization can now be done as:

Type
  TIntegerArray = Array of Integer;

var
  A : TIntegerArray;

begin
  A:=TIntegerArray.Create(1,2,3);
  Writeln(Length(A));
end.

Note that this will not work for dynamic arrays for which no type was created. That is, the following will not work:

var
  A : Array of Integer;

begin
  A:=Array of Integer.Create(1,2,3);
  Writeln(Length(A));
end.

This approach also works recursively for multi-dimensional arrays:

Type
  TIntegerArray = Array of Integer;
  TIntegerArrayArray = Array of TIntegerArray;

var
  A : TIntegerArrayArray;

begin
  A:=TIntegerArrayArray.Create(TIntegerArray.Create(1,2,3),
                               TIntegerArray.Create(4,5,6),
                               TIntegerArray.Create(7,8,9));
  Writeln('Length ',length(A));
end.

However, since it is a constructor (code is run at run-time) it is not possible to use this in an initialized variable syntax. That is, the following will not work:

Type
  TIntegerArray = Array of Integer;

var
  A : TIntegerArray = TIntegerArray.Create(1,2,3);

begin
  Writeln('Length ',length(A));
end.

Dynamic array constant expressions

As of version 3.2 of the compiler, an array can be constructed using an array expression in an explicit assignment or in an initialized variable. However, the expression is different. In an assignment statement, it resembles a set expression, in an initializd variable, the same syntax as for a constant array of fixed length must be used:

Type
  TIntegerArray = Array of Integer;

var
  A : TIntegerArray = (1,2,3);
  B : TIntegerArray;

begin
  B:=[3,4,5];
end.

Packing and unpacking an array

Arrays can be packed and bitpacked. Two array types which have the same index type and element type, but which are differently packed are not assignment compatible.

However, it is possible to convert a normal array to a bitpacked array with the pack routine. The reverse operation is possible as well; a bitpacked array can be converted to a normally packed array using the unpack routine, as in the following example:

Var
  foo : array [ 'a'..'f' ] of Boolean
    = ( false, false, true, false, false, false );
  bar : bitpacked array [ 42..47 ] of Boolean;
  baz : array [ '0'..'5' ] of Boolean;

begin
  pack(foo,'a',bar);
  unpack(bar,baz,'0');
end.

More information about the pack and unpack routines can be found in the system unit reference.