Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Sonstige Fragen zu Delphi (https://www.delphipraxis.net/19-sonstige-fragen-zu-delphi/)
-   -   Access Violation mit einfach verketteter Liste (https://www.delphipraxis.net/154649-access-violation-mit-einfach-verketteter-liste.html)

Hybrid666 19. Sep 2010 18:51


Access Violation mit einfach verketteter Liste
 
Moin,

ich habe folgende Struktur meiner Daten:
Delphi-Quellcode:
  PCommandList = ^TCommandList;
  TCommandList = record
    InputName : Integer;
    Pressed : Boolean;
    Next : PCommandList;
  end;

  PMakroList = ^TMakroList;
  TMakroList = record
    TimeStamp : Integer;
    Commands : PCommandList;
    Next : PMakroList;
  end;
Im Prinzip soll das einfach eine Einfach verktettete Liste sein (PMakroList), die eine Reihe von Commands enthält (PCommandList), auch als einfach verkettete Liste.

Das Anlegen der Liste funktioniert einwandfrei und ich habe auch eine Routine in meinem Objekt, das die Struktur speichern soll. Dazu habe ich folgenden Code:

Delphi-Quellcode:
procedure TMakro.SaveToFile(FileName : String);
var
  MRef : PMakroList;
  CRef : PCommandList;
  SaveFile : TextFile;
  I : Integer;
begin
  AssignFile (SaveFile, FileName);
  ReWrite (SaveFile);
  WriteLn (SaveFile, 'ÿNAME' + MakroName);
  WriteLn (SaveFile, 'ÿBEGINDESCRIPTION');
  for I := 0 to Description.Count - 1 do
    writeln (SaveFile, Description[I]);
  WriteLn (SaveFile, 'ÿENDDESCRIPTION');
  WriteLn (SaveFile, 'ÿBEGINMAKRO');
  MRef := Makro;
  while Assigned (MRef) do
  begin
    WriteLn (SaveFile, 'T' + IntToStr (MRef.TimeStamp));
    CRef := MRef.Commands;
    while Assigned (CRef) do
    begin
      writeln (SaveFile, IntToStr (CRef.InputName) + '|' + BoolToStr (CRef.Pressed, True));
      CRef := CRef.Next;
    end;
    MRef := MRef.Next;
  end;
  writeln (SaveFile, 'ÿENDMAKRO');
  CloseFile (SaveFile);

end;
(über den stil sowas zu speichern lässt sich streiten, daher bitte keine große diskussion drüber).

Wie man sieht wird einfach eine Textdatei hergenommen, erst diverse Optionen (name, beschreibung) von dem Objekt gespeichert und dann gehts an die List. Eigentlich dachte ich, dass ich genug kontrollabfragen drin hab, sodass ich nie ins leere laufen kann. Es sei noch gesagt, das alle leeren pointer mit "nil" initialisiert werden beim anlegen, damits bei assigned auch keine probleme gibt.

Nun tritt sporadisch (immer mal wieder) an folgender Stelle ein fehler auf:
Delphi-Quellcode:
WriteLn (SaveFile, 'T' + IntToStr (MRef.TimeStamp))
Und zwar ist der fehler eine Access violation. Ich gehe davon aus, das er auf MRef nicht zugreiffen kann. aber wo ist da mein fehler, bzw wo fehlt die kontrolle?

Danke für die Hilfe

Hybrid

mkinzler 19. Sep 2010 18:54

AW: Bekomme einen Error (sporadisch)
 
Überprüfe doch ob der Zeiger <> Nil is (Assigned())

Hybrid666 19. Sep 2010 18:56

AW: Bekomme einen Error (sporadisch)
 
Sollte doch eigentlich
Code:
while Assigned (MRef) do
tun, oder?

himitsu 19. Sep 2010 18:58

AW: Bekomme einen Error (sporadisch)
 
Abgesehn von der fehlenden Fehlerbehandlung für SaveFile,
welches aber mit diesem Problem nichts zu tun hat,
dürfte dieser Code OK sein,


also vermute ich eher einen Fehler beim Erstellen dieser Listen.


Und ja, bei Pointern entpicht
Delphi-Quellcode:
Assigned(P)
einem
Delphi-Quellcode:
P <> nil
.

mkinzler 19. Sep 2010 18:59

AW: Bekomme einen Error (sporadisch)
 
Eher
Delphi-Quellcode:
if Assigned( MRef) then ...
oder
Delphi-Quellcode:
if not Assigned( MRef) then Exit;

Hybrid666 19. Sep 2010 19:01

AW: Bekomme einen Error (sporadisch)
 
Hier ist der Code zum einfügen in die liste. Das ist der einzige Codeteil, der änderungen an der liste vornimmt:
Delphi-Quellcode:
procedure TMakro.AddInput (Pressed : Boolean; Name : Integer);
begin
  //Liste komplett leer
  if Makro = nil then
  begin
    new (Makro);
    Makro.Next := nil;
    RefMakro := Makro;
    new (RefMakro.Commands);
    Makro.TimeStamp := TimeStamp;
    RefCommand := Makro.Commands;
    Makro.Commands.InputName := Name;
    Makro.Commands.Pressed := Pressed;
  end
  else
  // TimeStamp existiert schon, also nur command in command liste einfügen
  begin
    if TimeStamp = RefMakro.TimeStamp then
    begin
      new (RefCommand.Next);
      RefCommand := RefCommand.Next;
      RefCommand.Next := nil;
      RefCommand.InputName := Name;
      RefCommand.Pressed := Pressed;
    end
    else
    // Timestamp größer als momentaner Referenztimestamp, also neuer Makro-Listen-Eintrag + neuen command einfügen
    begin
      new (RefMakro.Next);
      RefMakro := RefMakro.Next;
      RefMakro.Next := nil;
      RefMakro.TimeStamp := TimeStamp;
      new (RefMakro.Commands);
      RefCommand := RefMakro.Commands;
      RefCommand.Next := nil;
      RefCommand.InputName := Name;
      RefCommand.Pressed := Pressed;
    end;
  end;
end;
Zu sagen: Die Variablen Makro und TimeStamp existieren als private in der Klasse TMakro. TimeStamp ist ein Integer und Makro ist PMakroList. RefCommand und RefMakro existieren ebenfalls als private.

himitsu 19. Sep 2010 19:06

AW: Bekomme einen Error (sporadisch)
 
hmmmm, also wenn es wieder mal zu dieser Exception kommt, dann schau dir doch einfach im Debugger die Listen an.
Und vorallem die Stelle, an welcher dieses Problem auftreten tut.

Sir Rufo 19. Sep 2010 19:07

AW: Bekomme einen Error (sporadisch)
 
Da er aber schon prüft, ob das Object assigned ist, vermute ich, dass sich da ein Zeiger einschleicht, der zwar noch auch einer ehemaligen Struktur steht, diese Struktur ist aber schon wieder freigegeben. Der Zeiger wird dadurch ja nicht automatisch auf nil gesetzt.

Kapsel doch mal die Zugriffe mit try ... except und schau nach, bei welchem Object er diese Zicken macht und ob du evtl. da vorher was gelöscht hast.

Was auch gerne passiert:

Ein Object wird doppelt in eine Liste eingetragen. Dann einmal Free und aus der Liste gelöscht und fertig ist der Zugriffsfehler bei dem immer noch existierenden Eintrag in der Liste.
Delphi-Quellcode:
var
  objlst : TObjectList;
  obj : TObject;
begin
  objlst := TObjectList.Create;
  try
    obj := TObject.Create;
    objlst.Add( obj );
    objlst.Add( obj );
    objlst.Delete( 0 );
  finally
  end;
end;

Hybrid666 19. Sep 2010 19:08

AW: Bekomme einen Error (sporadisch)
 
peinliche frage: ich hab noch nie mit nem debugger geschafft. was nimmt man da und wie schau ich mir die listen an?

Danke!

mkinzler 19. Sep 2010 19:13

AW: Bekomme einen Error (sporadisch)
 
Einfach Breakpoint an der Stelle setzen (oder kurz vorher) dann hält der Debugger hier an. Dann kannst du die Variablen durch Hinzufügen als überwachte Ausdrücke oder durch Überfahren mit der Maus auswerten

himitsu 19. Sep 2010 19:16

AW: Bekomme einen Error (sporadisch)
 
Nja, erstmal wäre es praktisch, wenn du in deinem DP-Profil deine Delphiversion angibst oder diese in den Threads mit nennst, damit man geziehlt auf Besonderheiten dieser Version eingehen kann.

Aber so für den Anfang:
- Strg+Alt+W (oder irgendwo im Menü "Ansicht" den Punkt "Überwachte Ausdrücke")
> dort kann man Variablennamen angeben, deren Werte angezeigt werden sollen, wenn sich das Programm im Pause-Modus befindet (im Debugger angehalten)

- oder, ebenfalls wenn der Debugger das Programm angehalten hat, dann einfach mal den Mauszeiger für ein paar Sekunden über einen Variablennamen im Quellcode drüberhalten

angehalten ist z.B. das Programm, nachdem eine Exception ausgelöst wurde
oder wenn das Programm in einen Haltepunkt (F5) reingelaufen ist

Ansonsten kann man das Menü "Start" und die Abteilung "Debug-Fenster" im Menü "Ansicht" empfehlen.

Hybrid666 19. Sep 2010 19:20

AW: Bekomme einen Error (sporadisch)
 
supi, euch allen vielen dank! ich hab den fehler gefunden.

Delphi-Quellcode:
  if Makro = nil then
  begin
    new (Makro);
    Makro.Next := nil;
    RefMakro := Makro;
    new (RefMakro.Commands);
    Makro.TimeStamp := TimeStamp;
    RefCommand := Makro.Commands;
    RefCommand.Next := nil; //<--- Diese Zeile hier hat gefehlt, dadurch hat assigned wohl true geliefert...
    Makro.Commands.InputName := Name;
    Makro.Commands.Pressed := Pressed;
  end
EDIT: Sorry himitsu, werd ich gleich abändern. Ich arbeite mit dem alten rustikalen Delphi 7 *gg* Und danke für deine Tips!

mkinzler 19. Sep 2010 19:28

AW: Bekomme einen Error (sporadisch)
 
Aber auch der Code von dem "rustikalen" Delphi verdient es richtig gehighlightet zu werden. Verwende bitte für Delphi-Code den Delphi-Tag!

Luckie 19. Sep 2010 20:02

AW: Bekomme einen Error (sporadisch)
 
Und bitte gib deinem Beitrag einen aussagekräftigen Titel.

Hybrid666 19. Sep 2010 20:20

AW: Bekomme einen Error (sporadisch)
 
Zitat:

Zitat von Luckie (Beitrag 1050669)
Und bitte gib deinem Beitrag einen aussagekräftigen Titel.

ja das hab ich mir im nachhinhein auch gedacht, sorry. ich konnte ihn dann aber nichtmehr editieren.

Luckie 19. Sep 2010 20:22

AW: Bekomme einen Error (sporadisch)
 
Du kannst deine Beiträge und deren Titel innerhalb von 24 Stunden noch editieren. Es muss also gehen. Klick mal auf die Schaltfläche "Erweitert".

mkinzler 19. Sep 2010 20:25

AW: Bekomme einen Error (sporadisch)
 
Und danach kannst du den Beitrag mit der Bitte der Änderung melden. Einer vom Team wird deinem Wunsch dann nachkommen.

Hybrid666 19. Sep 2010 20:25

AW: Access Violation mit einfach verketteter Liste
 
Danke ;) nun sollte alles passen!


Alle Zeitangaben in WEZ +1. Es ist jetzt 05:57 Uhr.

Powered by vBulletin® Copyright ©2000 - 2024, Jelsoft Enterprises Ltd.
LinkBacks Enabled by vBSEO © 2011, Crawlability, Inc.
Delphi-PRAXiS (c) 2002 - 2023 by Daniel R. Wolf, 2024 by Thomas Breitkreuz