Einzelnen Beitrag anzeigen

Benutzerbild von MaBuSE
MaBuSE

Registriert seit: 23. Sep 2002
Ort: Frankfurt am Main (in der Nähe)
1.838 Beiträge
 
Delphi 10 Seattle Enterprise
 
#58

Re: Handling von Fehlern, Warnungen und Hints

  Alt 18. Sep 2008, 11:34
Hallo ich habe mal den Sachverhalt von Dezipaitor in einem kompilierbaren Beispiel verdeutlicht.

Vorgehensweise zum selbst Testen:
  • Quelltext der Unit komplett markieren und kopieren (Strg+C)
  • Neue Anwendung erstellen einen TButton drauf, ein TMemo drauf.
  • Den Button doppelklicken -> Quelltextfenster mit Methoden erscheint.
  • Kompletten Quelltext mit Strg+A markieren und durch Strg+V mit dem aus der Zwischenablage ersetzen.
  • Kopileren und debuggen.

Dei Kommentare im Code sollten alles erklären.

mfg
MaBuSE

Delphi-Quellcode:
unit Unit1;

interface

uses
  Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
  Dialogs, StdCtrls;

type
  TForm1 = class(TForm)
    Button1: TButton;
    Memo1: TMemo;
    procedure Button1Click(Sender: TObject);
  private
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

var
  Form1: TForm1;

// Hier werden die kompletten Funktionen definiert
// Im implementation Teil darf dann die Definition etwas abgekürzt werden
// (Z.B. darf der Rückgabetyp weggelassen werden.)
function TestIt1: Pointer;
function TestIt2: Pointer;
function TestIt3: Pointer;
function TestIt4: Pointer;

implementation

{$R *.dfm}

var
  _TestIt1: Pointer;
  _TestIt2: Pointer;

function TestIt1;
begin
  // Der Variable _TestIt1 wird das Ergebnis zugewiesen, das hat nix mit Result
  // zu tun -> Result ist undefiniert und war bei meinen Tests nicht immer nil!
  _TestIt1 := @Form1;
end;

function TestIt2;
begin
  // Der Variable _TestIt2 wird die Adresse der Funktion TestIt3 zugewiesen,
  // das hat nix mit Result zu tun -> Result ist noch undefiniert.
  _TestIt2 := @TestIt4;

  // Hier wird direkt in die TestIt4 Funktion gesprungen
  // -> also zu dem Result := @Form1 , danach wird nur ein RET duchgeführt.
  // Damit ist in dem EAX-Register zur Rückgabe der Wert @Form1 enthalten
  // und das Result wurde damit implizit gesetzt.
  asm
    JMP _TestIt2
  end;
end;

// Hier funktioniert es nicht, da der Compiler erkennt, das TestIt4 das
// EAX-Register verändert. Deshalb baut er beim Begin ein Push EBX ein
// Das Result würde nun in EBX gespeichert und beim end; wieder in EAX
// kopiert und der ursprüngliche EBX Wert wieder hergestellt (pop ebx).
// Damit ist das Result wieder undefiniert ;-)
function TestIt3;
begin // ASM: 53 - push ebx
  TestIt4; // ASM: E806000000 - call TestIt4
end; // ASM: 8BC3 - mov eax,ebx
              // ASM: 5B - pop ebx
              // ASM: C3 -ret

function TestIt4;
begin
  Result := @Form1; // ASM: B8C80C4600 - mov eax, @Form1
end; // ASM: C3 - ret

procedure TForm1.Button1Click(Sender: TObject);
var
  p: Pointer;
begin
  // Anmerkung die p := nil; Zeilen werden vom Compiler alle wegoptimiert.
  // Sie dienen nur der Übersichtlichkeit ;-)

  p := nil;
  p := TestIt1; // p ist undefiniert (kann, muß aber nicht nil sein)
  Memo1.Lines.Add('p = $'+ IntToHex(Integer(p),8));
  Memo1.Lines.Add('_TestIt1 = $'+ IntToHex(Integer(_TestIt1),8));

  p := nil;
  p := TestIt2; // in p steht das richtige Ergebnis
  Memo1.Lines.Add('p = $'+ IntToHex(Integer(p),8));
  Memo1.Lines.Add('_TestIt2 = $'+ IntToHex(Integer(_TestIt2),8));

  p := nil;
  p := TestIt3; // p ist undefiniert (kann, muß aber nicht nil sein)
  Memo1.Lines.Add('p = $'+ IntToHex(Integer(p),8));

  p := nil;
  p := TestIt4; // in p steht das richtige Ergebnis
  Memo1.Lines.Add('p = $'+ IntToHex(Integer(p),8));
end;

end.
(°¿°) MaBuSE - proud to be a DP member
(°¿°) MaBuSE - proud to be a "Rüsselmops" ;-)
  Mit Zitat antworten Zitat