Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Delphi:Is a temporary PChar guaranteed to have the same value after a string variable pointed by the PChar is changed?

The following code runs as expected in my system, but I'm not sure whether the P variable is guaranteed to have the same value after MyArray[0] is changed to a new value.

procedure Test;
var
  MyArray: array of string;
  P : PChar;

begin
  SetLength(MyArray, 2);
  MyArray[0] := 'ABCD';
  MyArray[1] := '1234';

  // Is P guaranteed to have the same value all the time?
  P := PChar(MyArray[0]);

  MyArray[0] := MyArray[1];
  MyArray[1] := P;

  WriteLn(MyArray[0]);
  WriteLn(MyArray[1]);
end;
like image 954
Astaroth Avatar asked Dec 29 '12 06:12

Astaroth


2 Answers

Your code is technically invalid. It only runs at all due to an implementation detail that should not be relied upon.

Let's take a look at the pertinent section of code:

P := PChar(MyArray[0]);
MyArray[0] := MyArray[1];
MyArray[1] := P;   

First we make P point to the first character of MyArray[0]. Then we assign to MyArray[0]. At this point there's no reason for the string buffer that P points at to be kept alive. No string variable refers to it. Its reference count has gone to zero, and so it should be deallocated. Which makes the subsequent use of P invalid.

In which case, why does your code run? Because the strings you use happen to be literals. And so they are stored with reference count equal to -1 and bypass the normal heap allocation routines used by strings. But if you were to use string values that were not literals, then what I describe in the paragraph above would come to pass. And I expect that your real code doesn't use literals.

So your actual question is somewhat moot. The P pointer just points at a block of memory. It remains pointing at the same block of memory until you modify the pointer. If you modify the contents of the block of memory, then P will see those modifications if you de-reference it. It's just a pointer like any other pointer.

You need to take care using a PChar variable. In your use, it is an unmanaged pointer into a compiler managed object. That provides lots of scope for errors and you've fallen into the trap. If you want a copy of a string, take a copy into another string variable.

like image 141
David Heffernan Avatar answered Nov 09 '22 02:11

David Heffernan


It seems that type casting from a string to PChar is different than taking its address. See the code below, it will take the address of string.

procedure Test;
var
  MyArray: array of string;
  P : ^String;

begin
  SetLength(MyArray, 2);
  MyArray[0] := 'ABCD';
  MyArray[1] := '1234';

  // take the pointer
  P := @MyArray[0];

  WriteLn(MyArray[0]);
  WriteLn(MyArray[1]);
  WriteLn(P^);

  // when content of array changes, P^ will change as well
  MyArray[0] := 'HELLO';
  WriteLn(P^);
end;
like image 39
Mehmet Fide Avatar answered Nov 09 '22 04:11

Mehmet Fide