1

My question is when I say the

char **szWords; IS szWords: PPAnsiChar;
char **szWords; IS szWords: array of array of AnsiChar;

also the

pfloat   *pScoreAcc; IS ScoreAcc: PSingle; 
pfloat   *pScoreAcc; IS ScoreAcc: array of Single; 

are they the same? In my case, array of array of AnsiChar; is working on my project and PPAnsiChar is not. I still want to know reason between the "P" and "array"

2 Answers 2

3

Be warned that in Delphi, a dynamic array is not exactly the same as a static array.

A static array is defined as such:

type
 TIntegerArray10 = array[0..10] of integer;
 PIntegerArray10 = ^TIntegerArray10;

And a dynamic array is defined as:

type
  TIntegerDynamicArray = array of integer;

But the dynamic array memory is allocated on the heap by Delphi, using the SetLength() function. You can access to the highest boundary with Length() or High() functions.

The dynamic array can then be mapped back to a static array, via the pointer() function:

var Dyn: TIntegerDynamicArray;
    i: integer;
begin
  SetLength(Dyn,11); // will create Dyn[0] to Dyn[10]
  for i := 0 to high(Dyn) do begin // same as  for i := 0 to length(Dyn)-1 do
    Dyn[i] := i;
    assert(PIntegerArray(pointer(Dyn))^[i]=i);
  end;
end;

The char * (pointers of char) can be handled as array of char in Delphi, but it's not the most easy way of doing it, because you'll have to get and free the memory at hand. You can use the AnsiString type, then cast it as a pointer: it will return a PAnsiChar.

So for an array of AnsiString, each array item (accessible by []) will be a PAnsiChar, and the pointer to the whole array of AnsiString will be a PPAnsiChar, i.e. a char **

var Dyn: array of AnsiString;
begin
  SetLength(Dyn,19);
  for i := 0 to 19 do
    Dyn[i] := IntToStr(i);
  // now pointer(Dyn) can be mapped to PPAnsiChar or char **
end;

All this is useful to create some content from Delphi, then use it in your C code.

For the contrary, i.e. using C generated char ** into Delphi, you must use pointers to static arrays, not pointer to dynamic arrays. As far as I remember, char ** structures usually end with a NULL to mark the end of array.

Here is what I would recommend to use, to convert a C char ** array into a true Delphi dynamic array (to be adapted to your purpose):

type
  TAnsiStringDynArray = array of AnsiString;
  TPAnsiCharArray = array[0..maxInt div 4-1] of PAnsiChar;
  PPAnsiCharArray = ^TPAnsiCharArray;

procedure ConvertCToDelphi(inArray: PPAnsiCharArray; var outDynArray: TAnsiStringDynArray);
var i, n: integer;
begin
  n := 0;
  if inArray<>nil then
    while inArray[n]<>nil do
      inc(n);
  SetLength(outDynArray,n);
  for i := 0 to n-1 do
    outDynArray[i] := AnsiString(InArray[n]); // explicit typing to make Delphi 2009 happy
end;

You can use PPAnsiChar, if you just want to enumerate a char ** array:

procedure Test(FromC: PPAnsiChar);
begin
  if FromC<>nil then
    while FromC^<>nil do begin
      writeln('Item value is ',FromC^); // Delphi will map FromC^ PAnsiChar into a string
      inc(FromC); // this will go to the next PAnsiChar in the array
    end;
end;
Sign up to request clarification or add additional context in comments.

4 Comments

You have to be careful when doing memory manipulations with dynamic arrays outside Delphi. Dynamic arrays are like pointers to a static array with 2 more values (reference count and high) stored before the beginning of the array data. So: 1) your C code should increment the reference count and decrement it when it doesn't need the array anymore; 2) changing the Delphi array's length in place using C (realoc) could lead to unexpected results; 3) if it is a dynarray of interfaces, variants or strings, then extra care should be taken as those types have magic compiler handling of it's refcount.
4) with the reference count mechanism there could be even more trouble as you could actually need to test if the refcount is zero and then free the array when needed inside your C code, else you may leak memory. A macro could be used to handle 1) and 4) easily tho.
@Trinidad: both points are true, theoretically. But in practice, I'll think that the C routines should make a copy of the given data if they need this data after they return to the Delphi code. In most cases, the C routine will consume the Delphi data, then return some results about the process. It's how SQLite3 works, and it's a common practice for libraries. So it's safe to call C code from Delphi, then let the reference count mechanism free the data when the variable is out of scope.
I feel so naive I didn't realize that at first, but you're right, making copies is the best approach. I think that should be stated in your answer for completeness.
-2

This is simply the difference between a pointer and an array.

This is more accurate:

char **szWords; IS szWords: PPAnsiChar;
char szWords[][]; IS szWords: array of array of AnsiChar;

3 Comments

is char **szWords and szWords[][]; the same? because i use char **szWords; is szWords: array of array of AnsiChar; and szWords: PPAnsiChar; is not working.
the given is actually char **szWords;
@XBasic3000: No, they aren't. One is a pointer to a pointer, and the other is an array of an array. I suggest you read up on pointers and arrays, because it is a little complicated to explain.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.