Zitat von
Luckie:
Und wie dann?
So wie dahead es vormacht.
Der Punkt ist folgender. Die Syntax mit dem PChar-Cast mag funktionieren (tut sie ja bekanntlich auch), sie mag sogar schneller sein (zumindest kann sie das unter bestimmten Umständen), aber @String[1] ist eine viel klarere Syntax.
Hier wird mit Compiler-Magic gespielt, ohne daß die meisten Leute wissen, was unter ihren Füßen vorgeht! Obwohl unnötig, entscheidet der Compiler über weitere Checks. Wie wir wissen, besitzen Strings einen Referenz- und einen Längenzähler.
Delphi-Quellcode:
type
StrRec = record
allocSiz: Longint;
refCnt: Longint;
length: Longint;
end;
Da soviel am AnsiString und WideString und String "managed" abläuft, kann man fast sagen, daß es sich verhält wie bei einer Klasse in C++. Dort kann man Operatoren auch überladen. In Delphi geht dies nicht, also hat der Compiler eine explizite Unterstützung für Strings eingebaut. Die kann man ihm nehmen, indem man die System-
Unit mal ein wenig ausmistet - aber das gehört hier nicht her.
Delphis Strings sind immer nullterminiert, sie sind also schonmal prinzipiell kompatibel mit PAnsiChar/PWideChar. Außerdem ist im Binärcode immer der Zeiger auf den Stringpuffer das Element, auf welches verwiesen wird. Die Offsets des o.g. Records werden also über negative Werte erreicht. Eigentlich ist also ein String in Delphi der Record von oben plus Zeiger auf den Puffer.
Und weil es so schön ist, habe ich mal folgende zwei Codeschnipsel in Delphi und im Disassembler verglichen:
Code:
var
StringVar: string;
dwSize: DWORD;
begin
dwSize := MAX_PATH;
SetLength(StringVar, MAX_PATH + 1);
GetComputerName([color=red]@StringVar[1][/color], dwSize);
Writeln(StringVar);
Readln;
end.
Code:
var
StringVar: string;
dwSize: DWORD;
begin
dwSize := MAX_PATH;
SetLength(StringVar, MAX_PATH + 1);
GetComputerName([color=red]PChar(StringVar)[/color], dwSize);
Writeln(StringVar);
Readln;
end.
Wie man im folgenden Code sieht, wird für jeden Typecast eine Funktion reingepappt. Wenn man sich die Funktionen anschaut, ist sogar die vom PChar-Cast (oft) schneller.
Code:
CODE:00408312 mov ds:dwSize, 260
CODE:0040831C mov eax, offset StringVar
CODE:00408321 mov edx, 261
CODE:00408326 call SetLength
CODE:0040832B push offset dwSize
CODE:00408330 mov eax, offset StringVar
[color=red]CODE:00408335 call @System@UniqueString$qqrr17System@AnsiString[/color]
CODE:0040833A push eax
CODE:0040833B call GetComputerNameA
CODE:00408340 mov eax, ds:ExceptionHandler
CODE:00408345 mov edx, ds:StringVar
CODE:0040834B call sub_4034A0
CODE:00408350 call _WRITELN
Code:
CODE:004082DE mov ds:dwSize, 260
CODE:004082E8 mov eax, offset StringVar
CODE:004082ED mov edx, 261
CODE:004082F2 call SetLength
CODE:004082F7 push offset dwSize
CODE:004082FC mov eax, ds:StringVar
[color=red]CODE:00408301 call @System@@LStrToPChar$qqrv[/color]
CODE:00408306 push eax
CODE:00408307 call GetComputerNameA
CODE:0040830C mov eax, ds:off_4092DC
CODE:00408311 mov edx, ds:StringVar
CODE:00408317 call sub_40346C
CODE:0040831C call _WRITELN
Was mich denn dann doch verwunderte, war daß auch bei der @String[1]-Syntax überhaupt eine extra Funktion aufgerufen wird. Diese wird benutzt um dem String, so er denn auf den Puffer eines anderen zugewiesenen Strings zeigt, eine eigene Kopie des eigentlichen Strings (i.e. Inhalt) zu verschaffen. Man muß sich also dazu klarmachen, daß ein String auch auf den Puffer eines ursprünglich anderen Strings zeigen kann. Bei einer Zuweisung wird dann der Zeiger zugewiesen, aber nicht der Puffer selbst kopiert. Das macht die Delphistrings natürlich äußerst effizient. Aber in unserem Fall verschafft es der klareren Syntax einen kleinen Nachteil, weil nämlich dieser Check erst dazwischengeschoben wird. Dieser Check wird offenbar forciert, wenn ich keinen Cast mache, sondern einen Pointer auf meinen String übergebe.
Fazit: Ich bleibe bei meiner klareren Syntax. Ihr dürft weiter PChar-Casts benutzen ... und dank obiger Erkenntnis sage ich auch nix böses mehr darüber
...
BTW: Vielleicht solltet ihr diesen Beitrag in die Codelib oder so schieben? Also splitten.