Delphi-PRAXiS

Delphi-PRAXiS (https://www.delphipraxis.net/forum.php)
-   Object-Pascal / Delphi-Language (https://www.delphipraxis.net/32-object-pascal-delphi-language/)
-   -   Delphi EAccessViolence bei Linearer Liste (https://www.delphipraxis.net/188463-eaccessviolence-bei-linearer-liste.html)

Riclem 6. Mär 2016 12:39

Delphi-Version: 2005

EAccessViolence bei Linearer Liste
 
Hey,

ich arbeite zur Zeit an einem Schulprojekt und komme bei einem Fehler einfach nicht weiter.
Ich habe eine FirstIn-FirstOut-Liste, bei der Elemente hinzugefügt und gelöscht werden. Für Hilfe bin ich wirklich dankbar. :cry:
Listendefinition:
Delphi-Quellcode:
type
  Zeiger = ^elementtyp;

  elementtyp = record
    Zeit_Kunde_an: integer;
    Zeit_Kunde_noetig: integer;
    ref: Zeiger;
  end;
Hinzufügen von Elementen
Delphi-Quellcode:
procedure FiFo(Anker: Zeiger);
var p, q: Zeiger;
  stop: integer;
begin
  stop := GetTickCount;
  while ((GetTickCount) < (stop + 1600)) and (Form1.Aufhoeren.checked=false) do
  begin
    new(q);
    q^.ref := nil;
    q^.Zeit_Kunde_an := GetTickCount;
    q^.Zeit_Kunde_noetig := 1000;
    if Anker = nil then
      Anker := q
    else
      p^.ref := q; //Hier war früher eine EAccessViolation - auf wundersame Weise verschwunden
    p := q;
    Form1.K1_Z.caption := IntToStr(StrToInt(Form1.K1_Z.caption) + 1);
    Pausieren(400);
  end;
end;
Löschen von Elementen
Delphi-Quellcode:
procedure Bearbeitung();
var K_Z_noetig: integer;
  p: Zeiger;
begin
  while Form1.Aufhoeren.checked = false do
  begin
    if (StrToINt(Form1.K1_Z.Caption) > 0) and (Form1.Aufhoeren.checked = false) then
    begin
      K_Z_noetig := K1_A^.Zeit_Kunde_noetig;
      Pausieren(K_Z_noetig);
      p := K1_A;
      K1_A := K1_A^.ref; //HIER tritt sie jetzt auf
      dispose(p);
      Form1.K1_Z.caption := IntToStr(StrToInt(Form1.K1_Z.caption) - 1);
    end
    else Pausieren(400);
  end;
end;
Viele Grüße und danke schonmal
Riclem

Zacherl 6. Mär 2016 12:56

AW: EAccessViolence bei Linearer Liste
 
Das
Delphi-Quellcode:
Anker
Argument muss schonmal als
Delphi-Quellcode:
var Anker: Zeiger
deklariert werden, damit die Zuweisung
Delphi-Quellcode:
Anker := q
durchgeführt werden kann. Die markierte Zeile knallt deshalb, weil zu diesem Zeitpunkt
Delphi-Quellcode:
p
noch nicht initialisiert ist.
Delphi-Quellcode:
p^.ref
geht deshalb ins Leere. Es war vermutlich
Delphi-Quellcode:
Anker^.ref := q
gewollt.

Riclem 6. Mär 2016 13:30

AW: EAccessViolence bei Linearer Liste
 
Ah klar stimmt, das
Delphi-Quellcode:
VAR
muss da natürlich hin. Zur Initialisierung von p: Da
Delphi-Quellcode:
p^.ref := q;
im else-Zweig steht, wird es erst beim zweiten Durchgang ausgeführt. Dann ist es doch über
Delphi-Quellcode:
p:=q;
schon initialisiert?
Hast du vielleicht für die zweite markierte Zeile noch eine Idee?

Vielen Dank bisher!!

Zacherl 6. Mär 2016 14:09

AW: EAccessViolence bei Linearer Liste
 
Zitat:

Zitat von Riclem (Beitrag 1332229)
Zur Initialisierung von p: Da
Delphi-Quellcode:
p^.ref := q;
im else-Zweig steht, wird es erst beim zweiten Durchgang ausgeführt. Dann ist es doch über
Delphi-Quellcode:
p:=q;
schon initialisiert?

Das ist natürlich korrekt .. zumindest wenn du implizierst, dass
Delphi-Quellcode:
Anker
initial immer
Delphi-Quellcode:
nil
ist. Dein Funktionsdesign (insbesondere die Logik deiner Funktionskapselung) ist leicht unintuitiv :lol:

An der zweiten Stelle wird
Delphi-Quellcode:
K1_A
zu irgendeinem Zeitpunkt wohl
Delphi-Quellcode:
nil
sein. Warum, weshalb, sehe ich grade im Gewurschtel der globalen Variablen und missbrauchten GUI Controls leider nicht.

Riclem 6. Mär 2016 14:18

AW: EAccessViolence bei Linearer Liste
 
Haha, ja so kann man das wohl nennen :D Dann werde ich das wohl selber suchen müssen. Sag mal, was bedeutet denn so eine EAccessViolation eigentlich so? Bei mir trat sie auf beim:
- Auslesen eines Elements der Liste
- bei Anweisungen à la x:=y^.ref
Gibt es da irgendwie bestimmte Ursachen oder so?
Vielen Dank :-D

Zacherl 6. Mär 2016 14:37

AW: EAccessViolence bei Linearer Liste
 
Eine Access Violation tritt immer dann auf, wenn du versuchst auf einen ungültigen Speicherbereich zuzugreifen (lesend oder schreiben). Ungültig ist ein Speicherbereich dann, wenn er vorher nicht allocated wurde.

Sowas passiert häuftig, wenn man beispielsweise mit Objekten arbeitet und vorher den
Delphi-Quellcode:
Create
Aufruf vergessen hat, oder wenn man Objekte bereits mit
Delphi-Quellcode:
Free
wieder zerstört hat, aber danach noch mit Ihnen arbeiten will. In deinem Falle ist die fehlerhafte Pointer-Arithmetik Schuld (präziser gesagt, wird dein Zeiger irgendwann entweder auf
Delphi-Quellcode:
nil
, oder eine andere nicht existente Speicherstelle zeigen. Sobald du dann mit
Delphi-Quellcode:
^
dereferenzierst, knallt es).

Generell ist es btw. immer eine gute Idee, Funktionalitäten in Klassen zu kapseln. Hier wäre auf die Schnelle mal eine an deine Delphi Version (keine Standard-Queue-Klasse, keine Generics) angepasste Implementierung einer FIFO-Liste (Queue):
Delphi-Quellcode:
type
  TQueueData = record
  public
    Foo: Integer;
    Bar: String;
  end;

  TQueue = class(TObject)
  strict private type
    PQueueItem = ^TQueueItem;
    TQueueItem = record
    public
      FLink: PQueueItem;
      BLink: PQueueItem;
      Data: TQueueData;
    end;
  strict private
    FFirst: PQueueItem;
    FLast: PQueueItem;
    FCount: Integer;
  public
    procedure Enqueue(const Data: TQueueData);
    function Dequeue: TQueueData;
    function Peek: TQueueData;
    procedure Clear;
  public
    destructor Destroy; override;
  public
    property Count: Integer read FCount;
  end;

{ TQueue }

procedure TQueue.Clear;
var
  Temp: PQueueItem;
begin
  while Assigned(FFirst) do
  begin
    Temp := FFirst^.FLink;
    Dispose(FFirst);
    FFirst := Temp;
  end;
  FLast := nil;
  FCount := 0;
end;

function TQueue.Dequeue: TQueueData;
var
  Temp: PQueueItem;
begin
  if (FCount = 0) then raise Exception.Create('No items in queue');
  Result := FFirst^.Data;
  Temp := FFirst^.FLink;
  Dispose(FFirst);
  if Assigned(Temp) then
  begin
    Temp^.BLink := nil;
    FFirst := Temp;
  end else
  begin
    FFirst := nil;
    FLast := nil;
  end;
  Dec(FCount);
end;

destructor TQueue.Destroy;
begin
  Clear;
  inherited;
end;

procedure TQueue.Enqueue(const Data: TQueueData);
var
  Item: PQueueItem;
begin
  New(Item);
  Item^.FLink := nil;
  Item^.BLink := FLast;
  Item^.Data := Data;
  if (not Assigned(FFirst)) then
  begin
    FFirst := Item;
  end;
  if Assigned(FLast) then
  begin
    FLast^.FLink := Item;
  end;
  FLast := Item;
  Inc(FCount);
end;

function TQueue.Peek: TQueueData;
begin
  if (FCount = 0) then raise Exception.Create('No items in queue');
  Result := FFirst^.Data;
end;
Auch ist es eigentlich immer schlecht, wenn man Business-Logik in die GUI reinwurschtelt. So ein Label als Counter zu verwenden, ist ein No-Go :P Dafür ist es einfach nicht gemacht, was man schon an deinem umständlichen
Delphi-Quellcode:
StrToInt
und
Delphi-Quellcode:
IntToStr
Gemenge sieht.

Riclem 6. Mär 2016 14:57

AW: EAccessViolence bei Linearer Liste
 
Wow super, vielen Dank. Jetzt klappt es :)
Danke, schönen Sonntag noch!


Alle Zeitangaben in WEZ +1. Es ist jetzt 08:36 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