Autor Beitrag
dr.eichhoernchen
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Fr 25.09.09 18:50 
Hallo

ich hab ein Problem und zwar löscht mein Prog nicht an der richtigen Stelle

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:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
type
telement = class
  private
    inhalt:integer;
    next: telement;
    prev: telement;
  public
    constructor create(z:integer);virtual;
end;

tliste = class
  private
    aktuell: telement;
    anfang: telement;
    ende: telement;
  public
    l:integer; //laenge der liste

...

procedure tliste.loeschen(i:integer);
var j:integer;
begin
  if l<=1 then begin
    anfang.free;
    anfang:=nil;
    ende:=nil;
    aktuell:=nil;
    l:=0;
  end;
  if l>1 then begin
    zeigeranfang;
    for j:=0 to (i-1do begin
      aktuell:=aktuell.next;
    end;
    if aktuell=ende then
      endeloeschen
    else
    if aktuell=anfang then
      anfangloeschen
    else
      begin
      aktuell.prev:=aktuell.next;
      aktuell.next:=aktuell.prev;
      aktuell.Free;
      l:=l-1;
      end;
  end;
end;


wenn ich das letzte Element anklicke, dann löscht es und wenn ich das erste Element anklicke, dann löscht es auch richtig. Wenn ich aber mittendrin anklicke, dann schreib es irgend ne Zahl ins Stringgrid..
Es liegt also vielleicht nur an den drei Zeilen

ausblenden Delphi-Quelltext
1:
2:
3:
      aktuell.prev:=aktuell.next;
      aktuell.next:=aktuell.prev;
      aktuell.Free;

aber wie soll ich die Zeiger denn anders verbiegen?

Ich wäre sehr dankbar um eine schnelle Antwort =)

Moderiert von user profile iconGausi: Delphi-Tags hinzugefügt
anbest
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 17

Win XP Prof. SP 3
Delphi 2006 Arch.
BeitragVerfasst: Fr 25.09.09 18:53 
Als aller erstes solltest du mal Tags für einen Delphi-Quellcode benutzen. Schreib einfach <span class="inlineSyntax"><span class="codecomment">{PROTECTTAG5ab48e23e0d1cb3ec5f1d34d479a331c}</span></span>
Heiko
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: Fr 25.09.09 19:57 
Hallo und :welcome: in der EE,

zuerst einmal: die 3 Zeilen die den Zeiger verbiegen sind imho korrekt (ich sehe zu mindestens gerade keinen Fehler). Aber ein ganz paar Kommentare zum Quelli habe ich doch ;).

user profile icondr.eichhoernchen hat folgendes geschrieben Zum zitierten Posting springen:

ausblenden Delphi-Quelltext
1:
    constructor create(z:integer);virtual;					

Wenn ich mich recht entsinne (arbeite mit Delphi inzwischen seit ~2 Jahren fast gar nicht mehr) ist virtual an der Stelle falsch. Wenn du es implementieren willst dann musst du schon override nehmen, da der constructor und destructor ja schon bei TObject definiert ist (wobei wenn ich mich sogar richtig entsinne: den TObject-Konstruktor musste man nicht überschreiben, aber den TObject-Destructor - schaue einfach mal auf die Hinweise). Und wenn du das Create gar nicht brauchst (also auch keine Methoden direkt am Objekt), nimm ein Record. Spart ein paar CPU-Takte. Da wir gerade bei den Objekten sind: wenn ich mich Recht entsinne musste man auch bei Klassen mit GetMem(ory) bzw. FreeMem arbeiten, damit es nicht zu problemen kommt (willkürlicher Inhalt der Objekte). Womöglich liegt bei dir da ein Fehler. (Wie gesagt: bin mir da nimmer sicher)


user profile icondr.eichhoernchen hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
  if l<=1 then begin
    anfang.free;
    anfang:=nil;
    ende:=nil;
    aktuell:=nil;
    l:=0;
  end;

Das ergibt ein schönes Speicherleck. Du löschst die ganze Liste in dem du das erste Element löschst. Was macste mit den anderen? Die liegen noch fein im RAM rum - bis das Programm beendet wird ;). Und gleich noch etwas: wenn ein ungültiger Wert übergeben wird, z.B. -1, lösche nicht gleich die ganze Liste, sondern gib ne Fehlermeldung mittels raise zurück. Wenn du unsauberen Code erlaubst, coden die Entwickler auch unsauber. Also sorge lieber dafür das es sauber ist ;).

user profile icondr.eichhoernchen hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
if l>1 then begin					

Es gibt else ;)

user profile icondr.eichhoernchen hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
zeigeranfang;					

Was macht diese Funktion?


user profile icondr.eichhoernchen hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
for j:=0 to (i-1do begin					

Nur ein kleiner Tipp (ist Geschmackssache): für den Menschen ist es besser lesbar 1 bis i, statt 0 bis i-1, da du auch sagst 1. Element ist das erste Element im Speicher. Falls es deine Spezifikation erlaubt: starte bitte bei Listen bei 0 und nicht bei 1. Ist in der Entwicklung eigentlich allgemein üblich das 0 das erste Element ist und -1 ein ungültiges. Ein gemische aus beiden verkompliziert das ganze nur unnötig (Strings in Delphi sind mit dem StartIndex von 1 eine ausnahme, da historisch bedingt früher das erste Byte die Stringlänge beinhaltete).

user profile icondr.eichhoernchen hat folgendes geschrieben Zum zitierten Posting springen:
ausblenden Delphi-Quelltext
1:
l:=l-1;					

Nur ein kleiner Tipp falls du es noch nicht kennst: dec(l); macht das gleiche wie l:=l-1 (vor allem bei längeren Variablen er höht es die Übersichtlichkeit - ist aber auch wieder Geschmacksache).

Ansonsten: Fehler sehe ich erst einmal keine
Tryer
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 226
Erhaltene Danke: 7



BeitragVerfasst: Fr 25.09.09 21:43 
ausblenden Delphi-Quelltext
1:
2:
3:
      aktuell.prev.next := aktuell.next;
      aktuell.next.prev := aktuell.prev;
      aktuell.Free;


Macht für mich mehr Sinn.

Grüsse, Dirk
Heiko
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: Fr 25.09.09 21:49 
user profile iconTryer hat folgendes geschrieben Zum zitierten Posting springen:
Macht für mich mehr Sinn.

Jo hast Recht - das ändern des Vorgängerzeigers bei dem zu löschenden Element bringt ja nicht viel.
dr.eichhoernchen Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Fr 25.09.09 23:43 
Vielen lieben Dank für eure schnellen Antworten =)



Aber zu dem "Speicherleck" wegen dem Liste löschen durch das erste Element:
da steht ja if l<=1 also ist die Liste schon leer oder hat nur noch ein Element übrig.


"Zeigeranfang" setzt meinen "aktuell"Zeiger in der Liste einfach auf das erste Element der Liste. Macht Sinn oder? ;) Ich bin kein Profi Programmierer..


soll ich jetzt 0 bis i-1 schreiben oder nicht? Ich habe bei 0 begonnen weil eben die meisten Sachen (sowie stringgrids oder arrays) bei 0 starten.


dec und inc sind mir bekannt, danke noch für den Tip, ich vergess es nur ständig ;)


Danke euch =)
Heiko
ontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic starofftopic star
Beiträge: 3169
Erhaltene Danke: 11



BeitragVerfasst: Fr 25.09.09 23:59 
Arg, hab das l als i interpretiert^^

user profile icondr.eichhoernchen hat folgendes geschrieben Zum zitierten Posting springen:
Aber zu dem "Speicherleck" wegen dem Liste löschen durch das erste Element:

Das mit dem Speicherleck bezog sich nur auf .Free. Aber wie ich gerade sehe arbeitest du ja mit tElement und nicht mit pElement. Von daher dürfte es doch kein Problem sein (wenn ich mit Zeigern arbeite, dann meistens, aus Gewohnheit, mit der p-Version - von daher meine ersten Bedenken ;) ). Überprüfen kannst du das Problem mit Speicherlecks übrigens ganz einfach: einfach FastMM4 einbinden - solange du aus der Delphi-IDE heraus das Programm startet meldet er sich schon bei Problemen beim beenden ;).

user profile icondr.eichhoernchen hat folgendes geschrieben Zum zitierten Posting springen:
da steht ja if l<=1 also ist die Liste schon leer oder hat nur noch ein Element übrig.

Jo hast Recht. Dadurch entsteht aber ein anderes Problem: Zweimal Free auf das gleiche (nicht mehr existierende) Element, falls die Funktion mehrfach aufgerufen wird.


user profile icondr.eichhoernchen hat folgendes geschrieben Zum zitierten Posting springen:
"Zeigeranfang" setzt meinen "aktuell"Zeiger in der Liste einfach auf das erste Element der Liste. Macht Sinn oder? ;) Ich bin kein Profi Programmierer..

Ja klar macht das Sinn. Nur lohnt es sich einen Einzeiler auszulagern? (Und muss aktuell bei dir Bestandteil der Klasse sein oder würde auch eine lokale Variable reichen? [sparrt bissl RAM ;) ]).


user profile icondr.eichhoernchen hat folgendes geschrieben Zum zitierten Posting springen:
soll ich jetzt 0 bis i-1 schreiben oder nicht? Ich habe bei 0 begonnen weil eben die meisten Sachen (sowie stringgrids oder arrays) bei 0 starten.

Ne ist schon richitg mit 0. Ich hatte halt das l>1 als i>1 (also index) gelesen, wes wegen ich dachte du fängst bei 1 erst an.
Aber daurch ein anderes Problem: Was passiert wenn du 2 Elemente bei dir einfügst, dann 2 löschst und dann wieder 2 einfügst? Denn wenn dein Start bei nil anfängt und das Ende auch dürfte es dann Probleme geben. Oder hast du es intern als Ring implementiert?
dr.eichhoernchen Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Sa 26.09.09 13:04 
Wenn ich zwei Elemente einfüge und lösche und wieder einfüge klappt das aber leider klappt das löschen an einer bestimmten Stelle immer noch nicht =(

meine prozedur sieht jetzt so aus :

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:
27:
28:
29:
procedure tliste.loeschen(i:integer);
var j:integer;
begin
  if l<=1 then begin
    anfang.free;
    anfang:=nil;
    ende:=nil;
    aktuell:=nil;
    l:=0;
  end;
  if l>1 then begin
    zeigeranfang;
    for j:=0 to (i-1do begin
      aktuell:=aktuell.next;
    end;
    if aktuell=ende then
      endeloeschen
    else
    if aktuell=anfang then
      anfangloeschen
    else
      begin
        aktuell.prev.next := aktuell.next;
        aktuell.next.prev := aktuell.prev;
        aktuell.Free;
        dec(l);
      end;
  end;
end;



aber vielleicht löscht das ja auch richtig und meine ausgabeprozedur ist falsch, die sieht wie folgt aus (klappt alledings bei allen andren buttons wie neues Element anhängen, an einer bestimmten Stelle einfügen, Ende löschen, Anfang löschen

ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
procedure TForm1.Ausgeben;
var i:integer;
begin
  if (not liste.listeleer) then begin
    stringgrid1.RowCount:=1;
    i:=0;
    liste.zeigeranfang;
    while not Liste.AmEnde do begin
      stringgrid1.RowCount:=i+2;
      stringgrid1.cells[0,i]:=inttostr(liste.gibinhalt);
      Liste.ZeigerWeiter;
      inc(i);
    end;
    stringgrid1.cells[0,i]:=inttostr(liste.gibinhalt);
  end
  else
    stringgrid1.cells[0,0]:='';
end;


das ist nicht sehr sauber programmiert, ich weiß. aber mir gehts erstmal darum, dass das ganze Ding läuft ;)
Tryer
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 226
Erhaltene Danke: 7



BeitragVerfasst: Sa 26.09.09 16: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:
23:
24:
25:
26:
27:
28:
29:
30:
procedure TListe.Loeschen(Index: Integer);
var 
  j: Integer;
  Akt: TElement;
begin
  if FCount <= 1 then 
  begin
    FreeAndNil(FAnfang);
    FEnde := nil;
    FCount := 0;
  end else 
  begin
    Akt := FAnfang;
    for j := 0 to Pred(Index) do 
      Akt := Akt.next;

    if FEnde = Akt then
      FEnde := Akt.prev 
    else
      Akt.next.prev := Akt.prev;

    if FAnfang = Akt then
      FAnfang := Akt.next
    else
      Akt.prev.next := Akt.next;

    Akt.Free;
    Dec(FCount);
  end;
end;


Ich finde das so besser zu lesen als wenn Du kleine Teile in andere Prozeduren auslagerst. Einen Wert wie "Aktuell" sollte man immer lokal deklarieren, dann hat man nämlich auch die Chance das ganze irgendwann threadsicher zu gestalten - und es wird übersichtlicher wenn man nicht überlegen muss wie denn die Funktion jetzt einen Zeiger verbiegt.

Wenn Du Dich bemühst von vornherein sauber zu programmieren ist die Chance das es mal läuft bedeutend grösser :D

Grüsse, Dirk


Zuletzt bearbeitet von Tryer am Sa 26.09.09 17:15, insgesamt 1-mal bearbeitet
dr.eichhoernchen Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Sa 26.09.09 16:46 
Ich verstehe deinen Quellcode nicht. Keine Ahnung was das alles zu bedeuten hat.Aber trotzdem danke.
Tryer
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 226
Erhaltene Danke: 7



BeitragVerfasst: Sa 26.09.09 16:57 
:gruebel: Schade.. was ist daran unverständlich?
Ob es funktioniert weis ich auch nicht, es ist nur hier reingetippt :twisted:

Ich habe die Felder etwas anders benannt, da ich ein "FCount" zum Beispiel passender finde als "l". Das vorangestellte "F" ist mir zur Gewohnheit geworden bei alle privaten Feldern einer Klasse.

Wenn Du überprüfen willst ob es so funktioniert male drei Kästchen auf ein Blatt Papier (unverzichtbar beim programmieren) und die Zeiger prev und next als Pfeile dazwischen. Dann kannst Du das ganze leicht nachvollziehen. Für jeden Befehl malst Du halt drei neue Kästchen und wie die Zeiger zu dem Zeitpunkt stehen.
dr.eichhoernchen Threadstarter
Hält's aus hier
Beiträge: 5



BeitragVerfasst: Sa 26.09.09 17:04 
ja natürlich, mit den kästen habe ich auch alle anderen prozeduren programmiert. malen hilft immer =) aber ich denke, dass es eben nur an den drei zeilen liegt

aktuell.next.prev:=aktuell.prev;
aktuell.prev.next:=aktuell.next;
aktuell.free;

weil das löschen am anfang und ende eben funktioniert
Tryer
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 226
Erhaltene Danke: 7



BeitragVerfasst: Sa 26.09.09 17:43 
Dann zeig uns mal EndeLoeschen, AnfangLoeschen und das Einfügen.
Das verbiegen der Zeiger um ein Element zu löschen ist so (unter der Voraussetzung das "Aktuell" richtig steht) korrekt.