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_______________________________________________
fpc-pascal maillist  -  fpc-pascal@lists.freepascal.org
http://lists.freepascal.org/mailman/listinfo/fpc-pascal

Reply via email to