Autor Beitrag
PrismaX
Hält's aus hier
Beiträge: 10


Embacadero RAD Studio 2010
BeitragVerfasst: Di 29.11.11 17:59 
Bei meinem Folgenden Quelltext zur Erstellung einer Doppeltverketteten Liste tritt ein AccessError auf

ausblenden 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 <> nildo      // Hier tritt der Fehler auf
  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


Zuletzt bearbeitet von PrismaX am Di 29.11.11 18:40, insgesamt 1-mal bearbeitet
Yogu
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: Di 29.11.11 18:21 
ausblenden 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); // test^ = nil
 new(test2);
 Stringlist := TStringlist.Create;
 for I := 1 to 44 do
  begin
    knoten^ := TKnoten.Create('',i,can);
 //knoten^ := knoten^.create('Test',i,can);
    knoten^.SetNext(test);
    if (i+1)< 45 then
    begin
    test2 := knoten;
    knoten := test; // knoten = test => knoten^ = test^ = nil (s.o.)
    knoten^.Previous := test2;
    new(test);
    new(test2);
    end
    else knoten^.Previous := test2;
  end;
  while (Knoten^.Previous <> nildo // koten^ = nil      // Hier tritt der Fehler auf
  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 Threadstarter
Hält's aus hier
Beiträge: 10


Embacadero RAD Studio 2010
BeitragVerfasst: Di 29.11.11 18:39 
ausblenden 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); // test^ = nil
 new(test2);
 Stringlist := TStringlist.Create;
 for I := 1 to 44 do
  begin
    knoten^ := TKnoten.Create('',i,can); //Knoten^ wird erstellt => nicht mehr nil 
    knoten^.SetNext(test);
    if (i+1)< 45 then
    begin
    test2 := knoten;
    knoten := test; // knoten = test => knoten^ = test^ = nil (s.o.)
    knoten^.Previous := test2;
    new(test);
    new(test2);
    end
    else knoten^.Previous := test2;
  end;
  while (Knoten^.Previous <> nildo // koten^ = nil      // Hier tritt der Fehler auf
  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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: Di 29.11.11 19:05 
Hups, ich habe deinen Code wohl nicht aufmerksam genug gelesen. Also, erstmal einrücken:

ausblenden 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 <> nildo      // Hier tritt der Fehler auf
  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?

ausblenden volle Höhe 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; // Previous ist nil bei i = 1

    if First = nil then
      First := Current;
    
    // Next wird immer im nächsten Durchlauf zugewiesen; für den letzten Knoten daher nicht mehr.
    if Previous <> nil then
      Previous^.Next := Current;

    // Diese letzte Zeile hat gefehlt, danke, PrismaX.
    Previous = Current;
  end;
end;


Zuletzt bearbeitet von Yogu am Di 29.11.11 20:40, insgesamt 1-mal bearbeitet
PrismaX Threadstarter
Hält's aus hier
Beiträge: 10


Embacadero RAD Studio 2010
BeitragVerfasst: 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?

ausblenden volle Höhe 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; // Previous ist nil bei i = 1

    if First = nil then
      First := Current;
    
    // Next wird immer im nächsten Durchlauf zugewiesen; für den letzten Knoten daher nicht mehr.
    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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: Di 29.11.11 20:49 
user profile iconPrismaX hat folgendes geschrieben Zum zitierten Posting springen:
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 Threadstarter
Hält's aus hier
Beiträge: 10


Embacadero RAD Studio 2010
BeitragVerfasst: 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):

ausblenden 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;         //Hilfsvariablen PKnoten = ^TKnoten, TKnoten ist eine class
  I: Integer;
begin
 previous := nil
 first := nil;

 for I := 1 to 44 do
  begin
    new(Knoten);

    knoten^ := TKnoten.Create('',i,can); // erstellen eines Objektes und der Pointer bekommt die Adresse zugewiesen --> falsch?
    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
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 2598
Erhaltene Danke: 156

Ubuntu 13.04, Win 7
C# (VS 2013)
BeitragVerfasst: Di 29.11.11 21:24 
user profile iconPrismaX hat folgendes geschrieben Zum zitierten Posting springen:
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:


ausblenden 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.