svět, kde se rodí nová budoucnost   
 
Delphi headDelphi Tips
 
 
Dynamic Array Sizing (old style)

Is there any way to dynamically redimension an array? When you dynamically allocate space for an array, you *can* dynamically allocate the size as needed.


type
  TIntegerArray = array[0..32767] of integer; 
  {Types don't allocate memory}

  PIntegerArray = ^TIntegerArray;
var
  I1,I2 : PIntegerArray;
begin
  GetMem(I1,500*SizeOf(Integer)); 
  {I1 now points to a 500 element array}

  GetMem(I2,1000*SizeOf(Integer));
  {I2 now points to a 1000 element array}

  ...

Ok, now *THIS* kind of variable-sized array has been available in Pascal for many a long year. That is, the kind where you decide the size at run-time but don't change it. Here is an example that works with records:  E.g.

TYPE
  VarArray = Array[0..65520 DIV SizeOf(MyRecord)] OF
                                              MyRecord;
  ptrVarArray = ^VarArray;
VAR
  MyArray : ptrVarArray;
  GetMem(MyArray, NumNeeded*SizeOf(MyRecord));


See? You define an array TYPE as large as possible, given the almost-64k limit on the size of a single variable. You define a pointer to that type. And you allocate just enough memory to hold the actual number needed. You can definitely use a TList too, but you'll need to define a simple OBJECT that holds your variant record, because TLists only hold TObjects and their descendants.

Here is a later post on C-Serve: ReAllocMem comes closest. You allocate your array on the heap with some incantations like

Type
  TIntArray = Array [0..High(Word) div Sizeof(Integer)
                                     
  -1] of Integer;
  {declares the maximum size possible for an array
   of Integers }

  PIntArray = ^TIntArray;

Procedure AllocArray( Var pArr: PIntArray; items: Word;
                      Var maxIndex: Word);
Begin
  If items > 0 Then Begin
    GetMem( pArr, items * Sizeof( Integer ));
    maxIndex := Pred( items );
  End
  Else
    pArr := Nil;
End;

Procedure ReDimArray(Var pArr : PIntArray;
                  newItems:
Word; Var maxIndex: Word);
Begin
  If pArr = Nil Then
    AllocArray( pArr, newItems, maxIndex )
  Else Begin
    ReAllocMem( pArr, Succ(maxIndex)*Sizeof(Integer),
                newItems*Sizeof(Integer));
    maxIndex := Pred( newItems );
  End;
End;

Procedure DisposeArray( Var pArr: PIntArray;
                                     maxIndex:
Word );
Begin
  FreeMem( pArr, Succ(maxIndex)*SizeOf(Integer));
End;

Var
  pMyArray: PIntArray;
  maxIndex, i: Word;

Begin
  try
    AllocArray( pMyArray, 100, maxIndex );
    For i:= 0 To maxIndex Do pMyArray^[i] := i;
    ...
    RedimArray( pMyArray, 200, maxIndex );
    For i:= 0 To maxIndex div 2 Do
     pMyArray^[Succ(maxIndex div 2)+i] :=
                                  
Sqr(pMyArray^[i]);
    ....
 finally
   DisposeArray( pMyArray, maxIndex );
 end;
..

"YIKES" i hear you say, "do i have to do this kind of gyrations for each array type i might need???". Well, you could, but it is not very difficult to write a set of generic procedures that will work for every base type you might use for an array. We assume that the array type is always declared with a lower bound of 0 and also use Cardinal instead of Word so the procedures will automagically expand to handle > 64K arrays under Delphi32.

Procedure AllocArray( Var pArr: Pointer; items,
 
itemsize: Cardinal; Var maxIndex: Cardinal);
Begin
  If items > 0 Then Begin
    GetMem( pArr, items * itemsize);
    maxIndex := Pred( items );
  End
  Else Begin
    pArr := Nil;
    maxIndex := 0;
    {WARNING! This is still an invalid index here! }
  End;
End;

Procedure ReDimArray( Var pArr: Pointer; newItems,
  
itemsize: Cardinal; Var maxIndex: Cardinal);
Begin
  If pArr = Nil Then
    AllocArray( pArr, newItems, itemsize, maxIndex )
  Else Begin
    ReAllocMem( pArr, Succ(maxIndex)*itemsize,
                   newItems*itemsize);
    maxIndex := Pred( newItems );
  End;
End;

Procedure DisposeArray( Var pArr: Pointer; itemsize,
  
maxIndex: Cardinal );
Begin
  FreeMem( pArr, Succ(maxIndex)*itemsize);
End;

To use these procedures to make a dynamic array of Double, for example, you would proceed as follows:

type
{we can directly declare a pointer to an array, no need to declare the array first}
  PDoubleArray = ^Array [0..High(Cardinal) div
                        
Sizeof(Double) -1] of Double;
Var
  pDbl: PDoubleArray;
  maxIndex, i: Cardinal;
  deg2arc: Double;

Begin
  deg2arc := Pi/180.0;
  try
    AllocArray(pDbl, 360, Sizeof(Double), maxIndex);
    For i:= 0 To maxIndex Do
      pDbl^[i] := Sin( Float(i) * deg2arc );
    ReDimArray(pDlb, 720, Sizeof(Double), maxIndex);

    For i:= 360 To maxIndex Do
      pDbl^[i] := Cos( Float(i-360) * deg2arc );
  finally
    DisposeArray( pDbl, Sizeof(Double), maxIndex );
  end;

And now the final icing: all this was typed off forehead; no idea if it will even compile <eg>! And a safety net feature is still missing: AllocArray and RedimArray should raise an exception if you try to allocate an array > 64Kbyte under Delphi16. I left this out since i'm not really familiar with these beasties (exceptions) yet. Delphi may do it anyway if range checking is enabled.

Here is Peter Below's approach: You allocate your array on the heap with some incantations like

Type
  TIntArray = Array [0..High(Word) div Sizeof(Integer)
                        
-1] of Integer;
  {declares the maximum size possible for an array
   of Integers }

  PIntArray = ^TIntArray;

Procedure AllocArray( Var pArr: PIntArray; items: Word;
                      Var maxIndex: Word);
Begin
  If items > 0 Then Begin
    GetMem( pArr, items * Sizeof( Integer ));
    maxIndex := Pred( items );
  End
  Else
    pArr := Nil;
End;

Procedure ReDimArray( Var pArr: PIntArray;
   newItems:
Word; Var maxIndex: Word );
Begin
  If pArr = Nil Then
    AllocArray( pArr, newItems, maxIndex )
  Else Begin
    ReAllocMem( pArr, Succ(maxIndex)*Sizeof(Integer),
                   newItems*Sizeof(Integer));
    maxIndex := Pred( newItems );
  End;
End;

Procedure DisposeArray( Var pArr: PIntArray;
   maxIndex:
Word );
Begin
  FreeMem( pArr, Succ(maxIndex)*SizeOf(Integer));
End;

Var
  pMyArray: PIntArray;
  maxIndex, i: Word;

Begin
  try
    AllocArray( pMyArray, 100, maxIndex );
    For i:= 0 To maxIndex Do pMyArray^[i] := i;
    ...
    RedimArray( pMyArray, 200, maxIndex );
    For i:= 0 To maxIndex div 2 Do
      pMyArray^[Succ(maxIndex div 2)+i] :=
                                  
Sqr(pMyArray^[i]);
    ....
 finally
 end;

"YIKES" I hear you say, "do I have to do this kind of gyrations for each array type i might need???". Well, you could, but it is not very difficult to write a set of generic procedures that will work for every base type you might use for an array. We assume that the array type is always declared with a lower bound of 0 and also use Cardinal instead of Word so the procedures will automagically expand to handle > 64K arrays under Delphi32.

Procedure AllocArray( Var pArr: Pointer; items,
                     itemsize):
Card

 

Back to Index of Tips

 

 

Send mail to radek.novak@infojet.cz with questions or comments about this web site.
Copyright © 1999-2002 Infojet.cz
Last modified: 27.07.2002