Dec 97 - Tips
Volume Number: 13 (1997)
Issue Number: 12
Column Tag: Tips and Tidbits
Tips and Tidbits
by Steve Sisak
Dynamically Sized Arrays in Think Pascal
Here's a tip for Think Pascal users, which has cost me a lot of time debugging every time I've crossed it:
Sometimes you need to create dynamically sized arrays, or arrays larger than the 32K you can build on the stack, or you just want your array in the heap rather than on the stack. The traditional way is to turn off range checking, define a type of array[0..0] of your record, then define a pointer (or even handle) to it, and access the array off the pointer or handle, ignoring the [0..0] bounds. This will fail with a skidding type error if you try to access beyond a 32K size in your array block. (By skidding error, its one of those that doesn't show up right away, but fails much later down the road, or at unusual places. Don't you hate skidding errors!)
What happens is that with the array[0..0] definition, the compiler thinks there's only one element (and technically, it's right!), and uses a sixteen bit value to construct the offset. Yes, that means the offset will overflow when you hit the 32K limit, and you'll start writing data in the memory space under your array, because the overflow will turn the offset negative. The way to fix this is to originally declare your array to be a large enough size so that the compiler will know it has to use a 32 bit offset. If you make it large enough so that you know you'll never, ever cross it, you can leave range check set on. It won't check the top end of your array, because you've made the number too large, but it will check the bottom.
Note also that if you detect this, and change the array bounds in the interface section of a unit, that it "will not change" the implementation in other units. You should force a complete recompile to be safe.
Here's some sample code, using a record with just two longs, but with enough records to cross the 32K limit. I've given constant values to some of the variables, but you can as easily use your own dynamic values:
type
landkey = record { use whatever record you want }
landsizeSF:longint; { I use two longints }
recID:longint;
end;
{ This code will fail if the following array bounds are changed to [0..0] }
landkeyArr = array[1..4000000] of landKey; { use a ridiculously large number }
landKeyArrPtr = ^landkeyArr;
var
theSortblock:landKeyArrPtr;
blockmarker, howmanyKeys:longint;
begin
howmanyKeys := 6000; { Actual array size, use what you need }
theSortBlock = landKeyArrPtr(NewPtr(sizeof(landKey) * howmanyKeys));
blockmarker := 5000; { will write beyond 32K -> 5000 * 8 = 40000 }
theSortBlock^[blockmarker].landSizeSF := 34343 * 5533; { stuff a value }
end;
Owen Hartnett
owenh@harlequin.com