2016-12-15 107 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

相關問題