Please consider the following program:
program SO41175184;
{$APPTYPE CONSOLE}
uses
SysUtils;
function Int9999: PAnsiChar;
begin
Result := PAnsiChar(AnsiString(IntToStr(9999)));
end;
function Int99999: PAnsiChar;
begin
Result := PAnsiChar(AnsiString(IntToStr(99999)));
end;
function Int999999: PAnsiChar;
begin
Result := PAnsiChar(AnsiString(IntToStr(999999)));
end;
function Str9999: PAnsiChar;
begin
Result := PAnsiChar(AnsiString('9999'));
end;
function Str99999: PAnsiChar;
begin
Result := PAnsiChar(AnsiString('99999'));
end;
function Str999999: PAnsiChar;
begin
Result := PAnsiChar(AnsiString('999999'));
end;
begin
WriteLn(Int9999); // '9999'
WriteLn(Int99999); // '99999'
WriteLn(Int999999); // '999999'
WriteLn(string(AnsiString(Str9999))); // '9999'
WriteLn(string(AnsiString(Str99999))); // '99999'
WriteLn(string(AnsiString(Str999999))); // '999999'
WriteLn(string(AnsiString(PAnsiChar(AnsiString(IntToStr(9999)))))); // '9999'
WriteLn(string(AnsiString(PAnsiChar(AnsiString(IntToStr(99999)))))); // '99999'
WriteLn(string(AnsiString(PAnsiChar(AnsiString(IntToStr(999999)))))); // '999999'
WriteLn(string(AnsiString(Int9999))); // '9999'
WriteLn(string(AnsiString(Int99999))); // '9999' <----- ?!
WriteLn(string(AnsiString(Int999999))); // '999999'
ReadLn;
end.
Only in one of these cases does the string lose a single character, in Delphi 2010 and Delphi XE3 both. With FPC the same program works correctly. Switching to PChar
also makes the problem disappear.
I suppose it has something to do with memory management, but I don't have enough of a clue where to look to do a meaningful investigation. Could anyone clarify?
Dynamically created strings are reference counted and deallocated when no references remain.
causes a temporary
AnsiString
to be created, its address taken via the cast toPAnsiChar
, and then the temporary string deallocated†. The resulting pointer points to now-unclaimed memory that may be overwritten for pretty much any reason, including during the allocations of more strings.Neither Delphi nor FPC clears memory by default during deallocations, so if the memory hasn't been re-used yet, you may get lucky when reading what used to be there. Or, as you saw, you may not.
When returning
PAnsiChar
like this, you need an agreement between caller and callee on memory management. You need to make sure you do not free the memory early, and you need to make sure your callers know how to free the memory afterwards.† Remy Lebeau points out that this deallocation happens when the procedure or function returns. If there is another statement after the assignment to
Result
, the string will still be available. This is normally correct, but there are also cases where it the temporary string gets deallocated before the return, for example when you create temporary strings in a loop. I would not recommend using temporary objects after the statement that creates them concludes, even in cases where it is valid, because it makes it too hard to verify whether the code is correct. For those cases, just use an explicit variable.