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
____________________________________________
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.
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.
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
____________________________________________
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.
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.
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.
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.
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.