2016-12-15 105 views
3

请考虑下面的程序:为什么这个PAnsiChar在转换为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. 

只有在这些情况下,一个没有串失去一个字符,在2010年德尔福和Delphi XE3两者。使用FPC,相同的程序可以正常工作。切换到PChar也会使问题消失。

我想这与内存管理有关,但我没有足够的线索去寻找有意义的调查。任何人都可以澄清?

回答

8

当没有引用保留时,动态创建的字符串被引用计数并释放。

Result := PAnsiChar(AnsiString(IntToStr(99999))); 

导致创建临时AnsiString,通过铸采取PAnsiChar其地址,然后将临时字符串释放。生成的指针指向现在未声明的内存,可能因任何原因被覆盖,包括在分配更多的字符串期间。

在释放期间,Delphi和FPC都不会默认清除内存,所以如果内存尚未被重新使用,那么在阅读曾经存在的内容时可能会很幸运。或者,如你所见,你可能不会。

当这样返回PAnsiChar时,需要调用者和被调用者之间对内存管理达成一致。您需要确保您不提前释放内存,您需要确保呼叫者知道如何释放内存。

Remy Lebeau指出,当程序或函数返回时会发生这种释放。如果在Result之后还有其他语句,字符串仍然可用。这通常是正确的,但也有在临时字符串在返回之前被释放的情况,例如当您在循环中创建临时字符串时。在创建它们的语句结束后,我不会推荐使用临时对象,即使在有效的情况下也是如此,因为它太难以验证代码是否正确。对于这些情况,只需使用一个显式变量。

+0

我可以理解推理,但我仍然觉得它很好奇它是如何出现在这些值中的一个。它在我的示例程序中发生,与我正在调试的程序中的方式完全相同。例如,我希望它有时可以工作,有时候不会。什么可以解释这种一致性和值之间的差异? –

+2

@ThijsvanDien分配器很难预测,但相当确定。如果它在你的程序的一次执行中表现出某种特定的方式,那么在所有执行过程中它都会有这样的表现。还有一点是'9999'的长度是4的平方倍数。内存块通常与4字节的边界对齐,这就解释了为什么部分覆盖会覆盖第五个字符。 – hvd

+0

@Remy不开始语句的词不应该大写。返回指针的函数应该担心所有的调用者,所以应该保持复数。关于具体什么时候发生释放是一个很好的注意事项,当我可以做一些额外的测试时,我会尝试将它重新引入。 – hvd

相关问题