Dec 97 - Tips
Volume Number: 13
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