Mega Code Archive

 
Categories / Delphi / Examples
 

Dynamicarrays

In Delphi 4 and higher, you can use a dynamic array: var MyArray: array of Integer; Note the lack of bounds. Use the SetLength procedure as you would on an AnsiString: SetLength(MyArray, 20); MyArray now behaves the same as an array[0..19] of Integer. The lower bound of a dynamic array is always 0. If you do not have Delphi4, then you can use pointers instead. Declare some types like this: type PInteger = ^Integer; // Windows.pas may aready define this one. PIntegerArray = ^TIntegerArray; TIntegerArray = array[0..0] of Integer; But this type only has 1 element! Not to worry, the compiler will never notice (except in one circumstance, which I'll mention later). Declare your array variable like this: var MyArray: PIntegerArray; Before you can use it, you must allocate some memory: GetMem(MyArray, 20 * SizeOf(Integer)); Ths allocates 80 bytes of memory, room for 20 Integers. You can access this like you would a normal array: for i := 0 to 19 do writeln(MyArray[i]); You may have to dereference the pointer (MyArray^[i])--I use D4, and dereferencing is optional there. You'll have to keep track of the array size yourself since Length won't work. Don't forget to call FreeMem when you're finished with your array. With the above type declaration, you won't be able to access the second element like this: MyArray[1]; the compiler will balk since the array index is out of bounds. There are two ways to fix that. One is to assign the index to a variable and use that, as shown in the for loop above. The ther solution is to change the type declaration slightly: TIntegerArray = array[0..1] of Integer; The upper bound doesn't really matter since you never declare any variable of type TIntegerArray, only PIntegerArray. Some people will declare an upper bound of 32767, but I think that's overkill. Just set the bound as high as you need since you only need it to make the compiler happy when you access elements at constant offsets. Wondering about that PInteger declaration? I almost forgot about it too. There is a fast way of walking through an array that doesn't require an offset counter all the time. var MyElement: PInteger; begin MyElement := MyArray; // This works since they're both pointers n := 20; while n > 0 do begin writeln(MyElement^); Inc(MyElement); Dec(n); end; end; This example doesn't exactly show off the advantages since it still requires the n counter to know when to stop, but it shows the key point. You can use the Inc and Dec operations on pointers and instead of just adding 1, the compiler will increase the pointer address by the size of what it points to. This is probably used most often in string (PChar) manipulation since you know you've reached the end of the array when MyElement^ = #0, meaning you never need to know the actual length of the array. *************************************************************************************************** Creating "Dynamic" Arrays? Is it possible to re-size arrays at runtime? I came across an example of how to do one. The author is Ben Licht, who presents an age-old Pascal method of creating a dynamic array. The trick is using a pointer to an array with a size of 1, then allocating memory for the pointer by multiplying the number of items you want in the array by the size of the array type. Here's a sample unit I've adapted from his example: {This unit demonstrates how to implement a dynArray} unit U; interface uses SysUtils, WinTypes, WinProcs, Classes, Controls, Forms, Dialogs, StdCtrls; type TResizeArr = array[0..0] of string; PResizeArr = ^TResizeArr; TForm1 = class(TForm) Button1: TButton; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; procedure DefineDynArray(var h : THandle; {Handle to mem pointer} NumElements : LongInt; {Number of items in array} var PArr : PResizeArr); {Pointer to array struct} procedure TestDynArray; implementation {$R *.DFM} {============================================================================ Procedure that defines the dynarray. Note that the THandle and Pointer to the array are passed by reference. This enables them to be defined outside the scope of this procedure. ============================================================================} procedure DefineDynArray(var h : THandle; {Handle to mem pointer} NumElements : LongInt; {Number of items in array} var PArr : PResizeArr); {Pointer to array struct} begin {Allocate Windows Global Heap memory} h := GlobalAlloc(GMEM_FIXED, NumElements * sizeof(TResizeArr)); PArr := GlobalLock(h); end; {============================================================================ Procedure that uses the DefineDynArray proc. This is pretty useless, but provides a good example of how you can access the elements of the 'array' once the array is defined. ============================================================================} procedure TestDynArray; var MyArray : PResizeArr; I : Integer; str : String; h : THandle; begin str := ''; DefineDynArray(h, 10, MyArray); {Define the 'array'} for I := 0 to 9 do MyArray^[I] := IntToStr(I); for I := 0 to 9 do str := str + MyArray^[I] + ','; ShowMessage(str); GlobalUnlock(h); {Must make a call to unlock the memory, then} GlobalFree(h); {free the memory and invalidate the handle} end; procedure TForm1.Button1Click(Sender: TObject); begin TestDynArray; end; end. This is a perfect example of one of those programming things that take hours to figure out, but turn out to be amazingly simple. But I should point out that it might just be simpler to use a TList, which does all of the above, but has methods to insert and delete items. It's only limited by the amount of memory you have. Copyright © 1997 Brendan V. Delumpa All Rights Reserved