Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Zugriffsverletzungs Fehler bei doppelt verketteter Liste
PrismaX - Di 29.11.11 17:59
Titel: Zugriffsverletzungs Fehler bei doppelt verketteter Liste
Bei meinem Folgenden Quelltext zur Erstellung einer Doppeltverketteten Liste tritt ein AccessError auf
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21:
| inherited Create; new(Knoten); new(test); new(test2); Stringlist := TStringlist.Create; for I := 1 to 44 do begin knoten^ := TKnoten.Create('',i,can); knoten^.SetNext(test); if (i+1)< 45 then begin test2 := knoten; knoten := test; knoten^.Previous := test2; new(test); new(test2); end else knoten^.Previous := test2; end; while (Knoten^.Previous <> nil) do Knoten := Knoten^.Previous; |
Fehler tritt wahrscheinlich tritt am Anfang des zweiten(!!) Durchlaufs auf. Meine Vermutung ist dass Knoten nicht initialisiert ist aber Warum? Kann einer weiterhelfen?
LG PrismaX
Yogu - Di 29.11.11 18:21
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| inherited Create; new(Knoten); new(test); new(test2); Stringlist := TStringlist.Create; for I := 1 to 44 do begin knoten^ := TKnoten.Create('',i,can); knoten^.SetNext(test); if (i+1)< 45 then begin test2 := knoten; knoten := test; knoten^.Previous := test2; new(test); new(test2); end else knoten^.Previous := test2; end; while (Knoten^.Previous <> nil) do Knoten := Knoten^.Previous; |
Soweit ich das sehe ist
test^ = nil, an der Stelle
knoten := test;, also ist
knoten^ in der
while-Bedingung auch
nil, oder?
PrismaX - Di 29.11.11 18:39
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22:
| 1: inherited Create; new(Knoten); new(test); new(test2); Stringlist := TStringlist.Create; for I := 1 to 44 do begin knoten^ := TKnoten.Create('',i,can); knoten^.SetNext(test); if (i+1)< 45 then begin test2 := knoten; knoten := test; knoten^.Previous := test2; new(test); new(test2); end else knoten^.Previous := test2; end; while (Knoten^.Previous <> nil) do Knoten := Knoten^.Previous; |
Allerdings erstelle ich den Knoten bei TKnoten.Create ==> theoretisch und praktisch ist er dann nicht mehr nil...
Außerdem ist es merkwürdig dass der Fehler erst beim zweiten Durchlauf der
while-Schleife Auftritt..
LG PrismaX
Yogu - Di 29.11.11 19:05
Hups, ich habe deinen Code wohl nicht aufmerksam genug gelesen. Also, erstmal einrücken:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| inherited Create; new(Knoten); new(test); new(test2); Stringlist := TStringlist.Create; for I := 1 to 44 do begin knoten^ := TKnoten.Create('',i,can); knoten^.SetNext(test); if (i+1)< 45 then begin test2 := knoten; knoten := test; knoten^.Previous := test2; new(test);
new(test2); end else knoten^.Previous := test2; end;
while (Knoten^.Previous <> nil) do Knoten := Knoten^.Previous; |
Der Fehler tritt also gar nicht im oberen Teil auf, sondern in der einzelnen
while-Schleife, in der du schlicht und einfach den ersten Knoten auswählst. Vorneweg, wenn ich das so richtig verstanden habe: Warum merkst du dir den nicht einfach zu Beginn?
Da der Fehler im zweiten Durchlauf der
while-Schleife auftritt, ist voher folgendes passiert:
- Knoten := Knoten^.Previous
- Aufgrund der ersten Bedingungsabfrage wissen wir, dass jetzt gilt: Knoten != nil.
- Allerdings ist nicht gesichert, ob Knoten^ != nil ist. Aufgrund der Exception ist also anzunehmen, dass irgend ein Element dieser Liste zwar Previous gesetzt hat, aber Previous^ nicht gesetzt ist.
Umgekehrter Ansatz: Im vorletzten Durchlauf der
for-Schleife haben wir am Ende ein neues, leeres
test2. Im letzten Durchlauf weisen wir dieses nun einem
knoten^.Previous zu. Dadurch ergibt sich, dass
knoten^.Previous^ nil ist. Also genau das, was wir oben festgestellt haben.
Hoffe, ich hab den Code jetzt richtig gelesen.
Edit: Dein Ansatz ist aber insgesamt ziemlich seltsam. Beispielsweise weist du die Eigenschaft
Previous zu, bevor du den Konstruktur aufrufst: Die Zuweisung in Zeile 15 bezieht sich ja schon auf das Objekt, das sich vorher hinter
test verborgen hatte, und noch gar nicht initialisiert war.
Edit2: Ist das hier vielleicht so in etwa, was du suchst?
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33:
| type TKnoten = record Prevoius: TKnoten; Next: TKnoten; end;
PKnoten = ^TKnoten;
var First, Current, Prevoius: PKnoten; i: Integer begin Previous := nil; First := nil;
for i := 1 to 44 do begin new(Current); Current^ := new TKnoten();
Current^.Previous := Previous; if First = nil then First := Current; if Previous <> nil then Previous^.Next := Current;
Previous = Current; end; end; |
PrismaX - Di 29.11.11 20:32
Also das Problem hast du jetzt richtig erkannt und dein Quelltext schaut auch erstmal gut aus.
Zu meinem Ansatz: Ja er ist z.T. merkwürdig (ich glaube meine erste implementation von Liste aus Delphi 7 von Doberenz/Gewinnus zu haben) aber für test und test2 wird schon am Anfang Speicher freigegeben d.h. eigentlich dürfte sie immer benutzbar sein.
Zu deinem Quelltext hätte ich eine Frage: Ist dann Previous nicht immer nil?
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31:
| type PKnoten = ^TKnoten; TKnoten = record Previous: PKnoten; Next: PKnoten; end;
var First, Current, Previous: PKnoten; i: Integer begin Previous := nil; First := nil;
for i := 1 to 44 do begin new(Current); Current^ := new TKnoten();
Current^.Previous := Previous; if First = nil then First := Current; if Previous <> nil then Previous^.Next := Current;
Previous := Current; end; end; |
So stell ich mir das vor damit wäre das Problem mit dem Previous := nil gelöst (hoffentlich nicht der gleiche Fehler wie in Post 1).
LG PrismaX
Yogu - Di 29.11.11 20:49
PrismaX hat folgendes geschrieben : |
| für test und test2 wird schon am Anfang Speicher freigegeben d.h. eigentlich dürfte sie immer benutzbar sein. |
Ist denn
TKnoten eine Klasse oder ein Record? Wenn's eine Klasse ist, musst du die ja erstmal initialisieren, so war meine Logik. Aber wenn du einen Record genommen hast, musst du das natürlich gar nicht machen; dann liegt der Fehler wohl doch wieder wo ganz anders.
Aber ich würde dir so oder so empfehlen, nochmal von vorne anzufangen (:mrgreen:). Überleg nochmal genau, was ein Schleifendurchlauf machen soll: Was der erste Durchlauf tun soll, der zweite, ..., der letzte. Wo genau do Speicher reservierst (in deinem Code passiert das zweimal pro Schleifendurchlauf). Wann welche Variable welchen Inhalt hat (und nenn sie nicht
test und
test2). Was am Ende tatsächlich rauskommen soll.
PrismaX - Di 29.11.11 21:08
Ja, TKnoten ist eine Klasse und bisher war ich davon ausgegangen dass zum erstellen von einem Objekt dieser Klasse die Construktor-Anweisung Create ausgereicht hätte --> dem ist anscheinend nicht so^^. Daher muss ich mich wohl nochmal stärker damit beschäftigen.
Zum anderen Thema (neuer Quelltext, neues Glück):
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24:
| var previous,first,current : PKnoten; I: Integer; begin previous := nil; first := nil;
for I := 1 to 44 do begin new(Knoten);
knoten^ := TKnoten.Create('',i,can); knoten^.Previous := previous;
if first = nil then first := knoten;
if previous <> nil then previous^.SetNext(Knoten);
previous := Knoten;
end; Knoten := first; |
Tut mir leid dass ich deine Zeit genutzt habe ich I***t hätte mir auch einfach den ersten Knoten merken können --> Problem gelöst^^.
Yogu - Di 29.11.11 21:24
PrismaX hat folgendes geschrieben : |
| Ja, TKnoten ist eine Klasse und bisher war ich davon ausgegangen dass zum erstellen von einem Objekt dieser Klasse die Construktor-Anweisung Create ausgereicht hätte --> dem ist anscheinend nicht so^^. |
Doch,
Create erzeugt ein Objekt einer Klasse. Das Problem ist nur, dass der Konstruktur in deinem Quelltext in einem bestimmten Fall nicht augerufen wurde: Und zwar im letzten Durchgang für
test. Das wurde nur mit
New initialisiert - was Platz für den Zeiger auf das Objekt schafft - aber nicht mit dem Konstruktur - was Platz für die eigentlichen Daten des Objekts freimacht.
Eigentlich ist es auch nicht unbedingt sinnvoll, einen Zeiger auf ein Objekt einer Klasse zu verwenden, dann hast du nämlich einen Zeiger auf einen Zeiger, und das macht alles so kompliziert. Entweder du lässt die Zeiger ganz sein und verwendest normale OOP, oder du machst's wie ich in meinem Beispiel und verwendest einen Record für
TKnoten.
Zu dem Quelltext: Das funktioniert auch mit einer Klasse, allerdings brauchst du dann keine Zeiger mehr:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26:
| type TKnoten = class Previous: TKnoten; Next: TKnoten; end;
var previous, first, current : TKnoten; I: Integer; begin previous := nil; first := nil;
for I := 1 to 44 do begin current := TKnoten.Create(); current.Previous := previous;
if first = nil then first := current;
if previous <> nil then previous.Next = current;
previous := current; end; end; |
Gefällt mir eigentlich auch besser. Die ganzen ^-Zeichen sind weg, und es ist reine Objektorientierung. Ich könnte jetzt spontan nichtmal einen Performancevorteil der Pointer-Lösung zeigen.
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2026 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!