Ich habe einige unerwartete Zugriffsverletzungen für Delphi-Code, die ich für richtig halte, aber scheint falsch zu kompilieren. Ich kann esFalscher Code bei der Kombination von anonymen und verschachtelten Prozeduren
reduzierenprocedure Run(Proc: TProc);
begin
Proc;
end;
procedure Test;
begin
Run(
procedure
var
S: PChar;
procedure Nested;
begin
Run(
procedure
begin
end);
S := 'Hello, world!';
end;
begin
Run(
procedure
begin
S := 'Hello';
end);
Nested;
ShowMessage(S);
end);
end;
Was für mich passiert ist, dass S := 'Hello, world!'
an der falschen Stelle speichert. Aus diesem Grund wird entweder eine Zugriffsverletzung ausgelöst, oder ShowMessage(S)
zeigt "Hello" (und manchmal wird eine Zugriffsverletzung ausgelöst, wenn die Objekte freigegeben werden, die zum Implementieren anonymer Prozeduren verwendet werden).
Ich verwende Delphi XE, alle Updates installiert.
Woher weiß ich, wo dies zu Problemen führt? Ich weiß, wie ich meinen Code umschreiben kann, um anonyme Prozeduren zu vermeiden, aber ich habe Probleme herauszufinden, in welchen Situationen sie zu falschem Code führen, also weiß ich nicht, wo ich sie vermeiden soll.
Es wäre interessant zu wissen, ob dies in späteren Versionen von Delphi behoben ist, aber nichts mehr als interessant, Upgrade ist keine Option zu diesem Zeitpunkt.
Auf QC, den neuesten Bericht kann ich die ähnliche #91876 finden, aber das ist in Delphi XE gelöst.
aktualisieren:
Basierend auf AlexSC Kommentare, mit einer geringfügigen Änderung:
...
procedure Nested;
begin
Run(
procedure
begin
S := S;
end);
S := 'Hello, world!';
end;
...
funktioniert.
Der erzeugte Maschinencode für
S := 'Hello, world!';
im andernfalls Programm ist
ScratchForm.pas.44: S := 'Hello, world!';
004BD971 B89CD94B00 mov eax,$004bd99c
004BD976 894524 mov [ebp+$24],eax
während die richtige Version
ScratchForm.pas.45: S := 'Hello, world!';
004BD981 B8B0D94B00 mov eax,$004bd9b0
004BD986 8B5508 mov edx,[ebp+$08]
004BD989 8B52FC mov edx,[edx-$04]
004BD98C 89420C mov [edx+$0c],eax
Der generierte Code in das fehlerhafte Programm ist nicht zu sehen, dass S
in eine vom Compiler generierte Klasseverschoben wurdeist wie auf externe lokale Variablen verschachtelter Methoden zugegriffen wird wie auf lokale Variablen zugegriffen wird.
In meinen Tests bekam ich diese Warnung "[DCC Warnung] Unit1.pas (45): W1036 Variable '$ Rahmen' wurde möglicherweise nicht initialisiert". Da ich keine $ -Frame-Variable deklariert habe, gehe ich davon aus, dass sie vom Compiler generiert wurde, als die Interfaces deklariert wurden, die die anonymen Methoden implementieren. Die Warnung deutet darauf hin, dass nicht alles vom Compiler korrekt ausgeführt wurde, es scheint also ein Fehler zu sein. Wenn Sie den Code so ändern, dass die S-Variable deklariert wird, wird das Problem früher angezeigt. Das Debugging legt nahe, dass die S-Variable vom generierten Code nicht korrekt behandelt wurde. – AlexSC
@AlexSC Die Erkennung "möglicherweise noch nicht initialisiert" ist notorisch schlecht, es gibt Unmengen von Fehlalarmen, die nicht auf ein echtes Problem hinweisen und keinen generierten Code beeinflussen. Daher sollte diese Warnung ignoriert werden. Ich kann diese Warnung (einschließlich der '$ frame'-Compiler-generierten Variable) auch in einfacherem Code erhalten, der richtig funktioniert. – hvd
Kompiliert und funktioniert ok in XE2 –