[fpc-pascal] Questions regarding arrays

Michael Müller mueller_michael at alice-dsl.net
Thu Sep 23 00:49:29 CEST 2010


Hi Graeme!

Am 22.09.2010 um 11:25 schrieb Graeme Geldenhuys:

> Hi,
> 
> I'm have some tough times with arrays (I use them very little). With
> trial and error I found that to use a pointer to an array of int32
> values returned by a C API (Xlib to be exact), I have to define it as
> follows in Object Pascal.
> 
> type
>  TAtomArray = array[0..0] of TAtom;
>  PAtomArray = ^TAtomArray;
> 
> var
>  xdndtypes: PAtomArray;
>  a: TAtom;  // just a culong type
> begin
>   ...
>    XGetWindowProperty(Display, FSrcWinHandle,
>        XdndTypeList, 0, 16000,
>        TBool(False),
>        AnyPropertyType,
>        @actualtype, @actualformat, @count, @remaining,
>        @xdndtypes);
>    // 'count' tells me the amount of items in the returned xdndtypes array
>    // so I can access the array items as follows
>    a := xdndtypes^[i]
>   ...
>   XFree(xdndtypes);
> end;
> 
> 
> Now if I change TAtomArray to the follow, then my code doesn't work. :-)
> 
>  TAtomArray = array of TAtom;   // a dynamic array
> 
> 
> So what exactly is the difference between these two?
> 
>    TAtomArray = array[0..0] of TAtom;
> vs
>    TAtomArray = array of TAtom;
> 
> 
> Is array[0..0] not actually a dynamic array, but just a static array
> with a single item?

Yes.

> If so, then why does the returned value from the C
> API call, which returns a pointer to an array of culong's (normally
> more that one item) work? Somewhere I'm getting confused with all
> this, but would love to understand it correctly, and why it works. :)

I assume you are running into an often made error when switching from static array to dynamic array that is used as a buffer and used with commands that expect a pointer to the buffer.

For a static array the compiler knows the size of the array since it can't be changed during runtime so it doesn't need to store the array length in an internal value. So a pointer to the static array points to the first element of the array.
But a dynamic array need to remember its actual size. So the internal data structure of a dynamic array will look like
TDynIntegerArray = record
  ArraySize: Integer;
  Array: ^Integer;
end;
The real array buffer can be at a complete different place in the memory.
So if you now send a pointer of TDynIntegerArray to a function that simply returns an array it will set the ArraySize to the value of the first array element. But the much more problematic part will be that if the array is bigger than SizeOf(TDynIntegerArray) it will overwrite memory that follows TDynIntegerArray and can lead to a memory access violation.

So what you need to do is to send the address of the first array element, in your case @xdndtypes[0].

But I don't know if this works well with an empty dynamic array. Especially since you can only set the array size by SetLength(). If in your case you do SetLength(xdndtypes, count) after the call I would expect that it will allocate memory for the array since the new size is greater than the old size (which is 0 for an unused dyn. array) and will overwrite the pointer of your function call array. Another problem is often to free memory in Pascal that was allocated by C.

The only case when I have used a dynamic array (or in the follow example a string which is a kind of dyn. char array) for a function call was when I set the array to a maximum size before the call, specify the maximum size in the call and reduced the array to the actual size after the call like:
Filename: String;
UsedLength: Integer;
SetLength(Filename, MAXPATH); 
GetWhateverFilenameFromTheSystem(@Filename[1], MAXPATH, @UsedLength); // String start at index 1;
SetLength(Filename, UsedLength); // or UsedLength - 1 when UsedLength includes a #0 as the char. array end indicator

So when the array memory is allocated by the calling function and freed by another function and you only what to be able to access the elements using the array braces you should use the static array approach. But I would define the static array type as follows
TAtomArray = array[0..MaxInt] of TAtom; // or whatever is the allowed maximum index
to avoid that Pascal will raise a range checking error when you activate range checking.

To prove my explanation you can simply print the memory addresses of @xdndtypes and @xdndtypes[0] for a static array and a (initialized?) dynamic array and you'll get the same addresses for the static array and different addresses for the dynamic.

Regards

Michael


More information about the fpc-pascal mailing list