Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass string to an array of PAnsiChar?

Something strange happens when I try to pass strings from the Lines of a TMemo control to an array of PChar. At the end of the routine, the last string in the array is duplicated. I was able to replicate this in this simple code:

procedure Test;
var
  i: smallint;
  arr: array of PAnsiChar;
  strarr: array[0..1] of string;
begin
  SetLength(arr, 2);
  strarr[0] := 'abbb';
  strarr[1] := 'baaa';

  for i := 0 to Length(strarr) do
    arr[i] := PAnsiChar(AnsiString(strarr[i]));
end;

If I run this procedure step by step, I can see arr[0] = 'abbb' however, at the end of the rutine, both values, arr[0] and arr[1] equal to baaa. I guess it has something to do with the typecast.

Can anyone see what is wrong ?

like image 659
Mario Villada Avatar asked Jan 24 '26 15:01

Mario Villada


1 Answers

There are two problems with your code:

  1. Your loop is exceeding the upper bound of the array. It needs to use for i := 0 to Length(strarr)-1 do or for i := 0 to High(strarr) do instead.

  2. More importantly, when you type-cast an AnsiString to a PAnsiChar, it returns a pointer to the AnsiString's internal data if the AnsiString is not empty. You are type-casting a UnicodeString to an AnsiString and grabbing a pointer into it, so the compiler has to use a compiler-generated local variable for the AnsiString data. In other words, your code is effectively doing the same thing as the following:

    procedure Test;
    var
      i: smallint;
      arr: array of PAnsiChar;
      strarr: array[0..1] of string;
      compiler_temp: AnsiString;
    begin
      SetLength(arr, 2);
      strarr[0] := 'abbb';
      strarr[1] := 'baaa';
      for i := 0 to Length(strarr) do
      begin
        compiler_temp := AnsiString(strarr[i]);
        arr[i] := PAnsiChar(compiler_temp);
      end;
    end;
    

Depending on how the memory for compiler_temp gets managed by the RTL memory manager at run-time, it is certainly possible for arr[0] and arr[1] to end up pointing at the same physical memory block in this situation.

If you want an array of PAnsiChar values then you need to start with an array of Ansi data for them to point at:

procedure Test;
var
  i: Integer;
  arr: array of PAnsiChar;
  strarr: array[0..1] of AnsiString;
begin
  SetLength(arr, 2);
  strarr[0] := 'abbb';
  strarr[1] := 'baaa';
  for i := 0 to Length(strarr)-1 do
    arr[i] := PAnsiChar(strarr[i]);
end;
like image 66
Remy Lebeau Avatar answered Jan 26 '26 09:01

Remy Lebeau



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!