Delphi Clinic C++Builder Gate Delphi Notes Weblog Delphi for .NET Prism for .NET
Dr.Bob's Delphi Notes Dr.Bob's Delphi Clinics
 Dr.Bob on Delphi Dynamic Arrays
See Also: Delphi Papers and Columns

Borland Delphi 4 features a number of Object Pascal language enhancements, as usual. In this article, I'll address a very handy language enhancement takes the ideas of Open Parameters and Long Strings back to the basics of arrays in the so-called Dynamic Arrays.

Dynamic Arrays
In Delphi 4, in addition to declaring static arrays such as

  X: Array[1..42] of String;

we can now also declare dynamic arrays. Dynamic arrays specify type information (the number of dimensions and the type of the elements) but not the number of elements. Thus

  X: Array of String;

  M: Array of Array of Integer;

declares two dynamic arrays. X is a one-dimensional arrays of Strings, while M is a two dimensional array of Integers (like a Matrix).

Dynamic arrays do not have a fixed size or length. Instead, memory for a dynamic array is (re-)allocated when we assign a value to the array or pass it to the SetLength procedure. Hence, the above declarations for X and M do not allocate memory. To create the array in memory, call SetLength. For example, given the declarations above,

  SetLength(X, 42);

allocates an array of 42 Strings, indexed 0 to 41. Dynamic arrays are always integer-indexed, always starting from 0.

After calling SetLength, the previous content of the dynamic array - if any - is copied along (so data never gets lost if we constantly increase or decrease the length of the array). Using the above knowledge, we can write a small - and very inefficient, of course - program to read a number of lines from a textfile, and only allocate the exact number of strings that are needed.

{$R+}
  {$APPTYPE CONSOLE}
  var
    X: Array of String;
    i: Integer;
  begin
    while not eof do
    begin
      SetLength(X,Length(X)+1); // very inefficient...
      readln(X[High(X)])
    end;
    for i:=0 to High(X) do writeln(X[i])
  end.

Dynamic-array variables are implicitly pointers and are managed by the same reference-counting technique used for Long Strings. To deallocate a dynamic array, assign nil to a variable that references the array or pass the variable to Finalize; either of these methods disposes of the array, provided there are no other references to it.

  {$R+}
  program Delphi4;
  {$APPTYPE CONSOLE}
  uses
    Dialogs;
  var
    X,Y: Array of String;
    i: Integer;
  begin
    SetLength(X, 7);
    Y := X;
    X[3] := 'Dynamic Arrays in Delphi 4';
    SetLength(X, 42);
    Y := X;
    SetLength(Y, 4);
    ShowMessage(Y[3]);
    X := nil;
    Finalize(Y);
  end.
Warning: we should not apply the dereference operator (^) to a dynamic-array variable or pass it to the New or Dispose procedure.

If X and Y are variables of the same dynamic-array type, X:=Y allocates X to the length of Y and points X to the same array as Y. Unlike strings, arrays are not automatically copied (i.e. made unique) before they are written to, but they keep pointed to the same - shared - memory area! For example, after this code executes

  var
    X,Y: array of String;
  begin
    SetLength(X, 1);
    X[0] := 'Hello, world';
    Y := X;
    Y[0] := 'Answer';
  end;

the value of X[0] is 'Answer'.

Unlike Long Strings, that get "split" when we change one of them (to get a unique copy), dynamic arrays keep pointed to the same area. A bit unexpected, perhaps, but at least we don't get delayed performance hits (like with Long Strings)...
Of course, since dynamic array contents are copied when we call SetLength, that's also all it takes (a call to SetLength) to create a unique copy of a dynamic array.

Assigning to a dynamic-array index (for example, X[42] := 'Answer') does not reallocate the array (we need to call SetLength to do that). Out-of-range indexes are not reported at compile time, but will raise an exception at run-time (with $R+ compiler directive).

When dynamic-array variables are compared, their references are compared, not their array values. Thus, after execution of the code

  var
    X, Y: array of String;
  begin
    SetLength(X,1);
    SetLength(Y,1);
    X[0] := 'Hello, world!';
    Y[0] := 'Hello, world!';
  end;

X = Y returns False but X[0] = Y[0] returns True.

To truncate a dynamic array, pass it to the Copy function and assign the result back to the array variable. For example, if X is a dynamic array, X := Copy(X, 0, 2) truncates all but the first 2 elements of X.

Once a dynamic array has been allocated, we can pass it to the standard functions Length, High, and Low. Length returns the number of elements in the array, High returns the array's highest possible index (Length - 1), and Low always returns 0. For a zero-length array, High indeed returns -1, so in that case High(X) < low(x).

To instantiate the multi-dimensional array M (see declaration on top of this paper), we need to call SetLength with two integer arguments:

  SetLength(M,10,5);

allocates an 10-by-5 array, and M[9,4] denotes an element of that array.

We can also create multidimensional dynamic arrays that are not rectangular. The first step is to call SetLength, passing it parameters for the first n dimensions of the array. For example,

  var
    M: array of array of Integer;
  begin
    SetLength(M,10);

allocates ten rows for M but no columns. Then, we can allocate the columns one at a time (giving them different lengths); for example

  SetLength(M[2], 42);

makes the third column of M 42 integers long. At this point (even if the other columns haven't been allocated) we can assign values to the third column for example, M[2][41] := 7.


This webpage © 2000-2010 by Bob Swart (aka Dr.Bob - www.drbob42.com). All Rights Reserved