// EDIT:
Um hier keine unbewiesenen Behauptungen zu posten... kleines Beispiel:
Delphi-Quellcode:
function Test(const Value: string): PChar;
begin
Result := PChar(DupeString(Value, 10));
end;
procedure TForm240.FormCreate(Sender: TObject);
var
TestResult1, TestResult2: PChar;
begin
TestResult1 := Test('a');
TestResult2 := Test('b');
ShowMessage(TestResult1);
ShowMessage(TestResult2);
end;
Du wirst sehen, dass (zumindest unter XE) zweimal 10xb ausgegeben wird. Einfach weil die selbe Speicheradresse wiederverwendet wird bevor der erste String ausgegeben wird... Und das ist ja auch vollkommen korrekt, da der Speicherbereich schon wieder freigegeben ist. Dass darauf noch ein Pointer existiert, darf nicht passieren. Deshalb ist der Code so schlicht fehlerhaft.
Das aber auch nur, wenn der Speicher in der Zwischenzeit nicht wieder (komplett) freigegeben oder überschrieben wird.
Denn FastMM behält sich Speicher noch eine Weile, um ihn schneller wiederverwenden zu können und nicht jedesmal langwierig bei Windows beantragen zu müssen.
DupeString liefert eine Stringvariable zurück, wofür Delphi eine temporäre Variable anlegt, welche bei Funktionsende wieder freigegeben wird.
Delphi-Quellcode:
function Test(const Value: string): PChar;
var
Temp: string;
begin
Temp := DupeString(Value, 10);
Result := PChar(Temp);
end; // und im FunktionsENDe steht quasi ein Temp := '';
Und es ist Zufall, wenn hier wieder die selbe PChar-Adresse zurückgegeben wird.
Nämlich nur dann, wenn FastMM zufällig den selben Speicherbereich für den Result-String von DupeString wiederverwendet. (was bei einer so einfachen Funktion und bei SingleThreadding zufällig etwas öfters passieren kann, da die Berechnung/Ermittling des nächst freien Speicherblocks ja gleich abläuft)
Tipp: Versuch es mal mit einem größeren String, wie z.B.
DupeString(Value, 1000000000);
, denn da ist es wahrscheinlich, daß FastMM den "großen" Speicher sofort wieder an Windows zurückgibt.
Sowas wäre eher möglich, aber das ist natürlich nicht für Multithread, also nicht für mehrere gleichzeitige Aufrufe dieser Funktion, da es nur einen StringSpeicher gibt.
Delphi-Quellcode:
var
Test_Result: string; // den String auch noch nach dem Funktionsende erhalten
function Test(const Value: string): PChar;
begin
Test_Result := DupeString(Value, 10);
Result := PChar(Test_Result);
end;
procedure TForm240.FormCreate(Sender: TObject);
var
TestResult1, TestResult2: string; // Rckgabewerte sofort in einen String umwandeln, also kopieren
begin
TestResult1 := Test('a');
TestResult2 := Test('b');
ShowMessage(TestResult1);
ShowMessage(TestResult2);
end;
Einfacher ist ShareMem und direkte Stringübergabe, oder die Verwendung von WideString.
WideString ist eine Kaspelung von String-APIs der OleAut32.dll (
SysAllocStringLen SysStringLen SysFreeString) und ist somit auch immer gleich und unabhängig vom Delphi-Speichermanager.
Schlecht ist es auch, wenn keine generischen Typen verwendet werden.
PChar und String können sich je nach Compiler (z.B. vor und
nach ab Delphi 2009) verändern und schon verändert sich auch die Schnittstelle (PAnsiChar <> PWideChar | AnsiString <> UnicodeString), aber Schnittstellen müssen statisch/unveränderlich sein.