为什么这个 PAnsiChar 在转换为 AnsiString 时会被切碎?
Why does this one PAnsiChar get chopped when converted to AnsiString?
请考虑以下方案:
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.
只有在其中一种情况下,字符串才会丢失一个字符,在 Delphi 2010 和 Delphi XE3 中都是如此。使用 FPC,相同的程序可以正常工作。切换到 PChar
也会使问题消失。
我想它与内存管理有关,但我没有足够的线索去寻找有意义的调查。谁能澄清一下?
动态创建的字符串被引用计数并在没有引用时被释放。
Result := PAnsiChar(AnsiString(IntToStr(99999)));
导致创建临时 AnsiString
,其地址通过强制转换为 PAnsiChar
,然后释放临时字符串 †。生成的指针指向现在无人认领的内存,该内存可能出于几乎任何原因被覆盖,包括在分配更多字符串期间。
默认情况下,Delphi和FPC在释放期间都不会清除内存,所以如果内存还没有被重新使用,你可能会幸运地读取曾经存在的内容。或者,如您所见,您可能不会。
当像这样returning PAnsiChar
时,需要调用者和被调用者就内存管理达成一致。您需要确保您没有提前释放内存,并且您需要确保您的调用者知道之后如何释放内存。
† Remy Lebeau 指出,这种释放发生在过程或函数 return 时。如果在对 Result
赋值后还有另一条语句,该字符串仍然可用。这通常是正确的,但也有临时字符串在 return 之前被释放的情况,例如当您在循环中创建临时字符串时。我不建议在创建临时对象的语句结束后使用临时对象,即使在它有效的情况下也是如此,因为这样很难验证代码是否正确。对于这些情况,只需使用一个显式变量。
请考虑以下方案:
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.
只有在其中一种情况下,字符串才会丢失一个字符,在 Delphi 2010 和 Delphi XE3 中都是如此。使用 FPC,相同的程序可以正常工作。切换到 PChar
也会使问题消失。
我想它与内存管理有关,但我没有足够的线索去寻找有意义的调查。谁能澄清一下?
动态创建的字符串被引用计数并在没有引用时被释放。
Result := PAnsiChar(AnsiString(IntToStr(99999)));
导致创建临时 AnsiString
,其地址通过强制转换为 PAnsiChar
,然后释放临时字符串 †。生成的指针指向现在无人认领的内存,该内存可能出于几乎任何原因被覆盖,包括在分配更多字符串期间。
默认情况下,Delphi和FPC在释放期间都不会清除内存,所以如果内存还没有被重新使用,你可能会幸运地读取曾经存在的内容。或者,如您所见,您可能不会。
当像这样returning PAnsiChar
时,需要调用者和被调用者就内存管理达成一致。您需要确保您没有提前释放内存,并且您需要确保您的调用者知道之后如何释放内存。
† Remy Lebeau 指出,这种释放发生在过程或函数 return 时。如果在对 Result
赋值后还有另一条语句,该字符串仍然可用。这通常是正确的,但也有临时字符串在 return 之前被释放的情况,例如当您在循环中创建临时字符串时。我不建议在创建临时对象的语句结束后使用临时对象,即使在它有效的情况下也是如此,因为这样很难验证代码是否正确。对于这些情况,只需使用一个显式变量。