| Autor |
Beitrag |
hummel
Hält's aus hier
Beiträge: 3
|
Verfasst: Di 14.06.05 16:38
Warum können diese Prozeduren nicht aufgerufen werden?
Habe ein Programm, welches Daten in den Heap schreibt. Diese sollten nun durch einen Klick auf den entsprechenden Button entweder nach dem „Namen“ (Procedure „sortn“) oder nach dem „Einkommen“ (Procedure „sorte“ sortiert werden.
Nur läuft das Programm nicht bzw. nur solange man keine „Sortierbutton“ betätigt. Ansonsten begibt essich in eine Endlosschleife und stürzt ab.
Kann mir jemand den Fehler zeigen bzw. den Quelltext entsprechend korigiernen?
Danke und Gruß
Hummel
Hier der Quelltext:
PS: hab das Programm mal in den Anhang gepackt.
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: 50: 51: 52: 53: 54: 55: 56: 57: 58: 59: 60: 61: 62: 63: 64: 65: 66: 67: 68: 69: 70: 71: 72: 73: 74: 75: 76: 77: 78: 79: 80: 81: 82: 83: 84: 85: 86: 87: 88: 89: 90: 91: 92: 93: 94: 95: 96: 97: 98: 99: 100: 101: 102: 103: 104: 105: 106: 107: 108: 109: 110: 111: 112: 113: 114: 115: 116: 117: 118: 119: 120: 121: 122: 123: 124: 125: 126: 127: 128: 129: 130: 131: 132: 133: 134: 135: 136: 137: 138: 139: 140: 141: 142: 143: 144: 145: 146: 147: 148: 149: 150: 151: 152:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type ZEIGER=^ELEMENT; ELEMENT=RECORD NAME :STRING[15]; EINKOMMEN :integer; NEXTN,NEXTE:ZEIGER; END;
TForm1 = class(TForm) GroupBox1: TGroupBox; Edit1: TEdit; GroupBox2: TGroupBox; Edit2: TEdit; GroupBox3: TGroupBox; ListBox1: TListBox; ListBox2: TListBox; Button2: TButton; Button3: TButton; GroupBox4: TGroupBox; Button1: TButton; Button4: TButton; procedure Button1Click(Sender: TObject); procedure Button2Click(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button4Click(Sender: TObject); private public procedure eingabe;
end;
var Form1: TForm1; KOPFE,KOPFN:ZEIGER; wahl:char; implementation
{$R *.dfm}
PROCEDURE SORTE(NEU:ZEIGER); VAR Z,ZNFG:ZEIGER; GEFUNDEN:BOOLEAN; BEGIN ZNFG:=KOPFE; GEFUNDEN:=FALSE; WHILE (ZNFG<>NIL) AND NOT GEFUNDEN DO BEGIN IF ZNFG^.EINKOMMEN >NEU^.EINKOMMEN THEN GEFUNDEN:=TRUE ELSE BEGIN Z:=ZNFG; ZNFG:=ZNFG^.NEXTE; END; END; IF ZNFG=KOPFE THEN BEGIN NEU^.NEXTE:=KOPFE; KOPFE:=NEU; END ELSE BEGIN NEU^.NEXTE:=Z^.NEXTE; Z^.NEXTE:=NEU; END; END;
PROCEDURE SORTN(NEU:ZEIGER); VAR Z,ZNFG:ZEIGER; GEFUNDEN:BOOLEAN; BEGIN ZNFG:=KOPFN; GEFUNDEN:=FALSE; WHILE (ZNFG<>NIL) AND NOT GEFUNDEN DO BEGIN IF ZNFG^.NAME >NEU^.NAME THEN GEFUNDEN:=TRUE ELSE BEGIN Z:=ZNFG; ZNFG:=ZNFG^.NEXTN; END; END; IF ZNFG=KOPFN THEN BEGIN NEU^.NEXTN:=KOPFN; KOPFN:=NEU; END ELSE BEGIN NEU^.NEXTN:=Z^.NEXTN; Z^.NEXTN:=NEU; END; END;
PROCEDURE TForm1.EINGABE; VAR NEU:ZEIGER;
BEGIN wahl:='n'; NEW(KOPFN); KOPFN^.NEXTN:=NIL; KOPFN^.NEXTE:=NIL; KOPFN^.NAME:=edit1.text; KOPFN^.EINKOMMEN:=strtoint(edit2.text); WHILE UPCASE(WAHL) <>'N' DO BEGIN NEW(NEU); KOPFN^.NAME:=edit1.text; KOPFN^.EINKOMMEN:=strtoint(edit2.text); SORTE(NEU); SORTN(NEU); END; END;
PROCEDURE ausgabee(z:zeiger); BEGIN
if Z <> NIL then begin Form1.Listbox1.items.add(Z^.Name + ' - ' + floattostr(Z^.Einkommen)); Ausgabee(Z^.nexte); end; end;
PROCEDURE AUSGABEN(z:zeiger); BEGIN if Z <> NIL then begin Form1.Listbox2.items.add(Z^.Name + ' - ' + floattostr(Z^.Einkommen)); Ausgaben(Z^.nextn); end; end;
procedure TForm1.Button2Click(Sender: TObject); begin eingabe; AUSGABEN(KOPFN); AUSGABEE(KOPFE); end;
procedure TForm1.Button1Click(Sender: TObject); begin sortn(kopfn); end;
procedure TForm1.Button4Click(Sender: TObject); begin sorte(kopfe); end; procedure TForm1.Button3Click(Sender: TObject); begin halt; end;
end. |
Moderiert von Gausi: Delphi-Tags hinzugefügt.
Einloggen, um Attachments anzusehen!
|
|
Narses
      

Beiträge: 10185
Erhaltene Danke: 1261
W11x64
TP3 .. D7pro .. D10.2CE
|
Verfasst: Sa 18.06.05 23:34
Moin!
hummel hat folgendes geschrieben: | Kann mir jemand den Fehler zeigen bzw. den Quelltext entsprechend korigiernen?
|
Tja, du hast da schon nicht mehr ganz triviales Zeug angeschnitten, deshalb hat da auch bisher keiner drauf geantwortet...  Kannst von Glück reden, dass ich meine "Kenntnisse" über lineare Listen und Zeiger mal wieder etwas "auffrischen" wollte, sonst hätte ich mir die Arbeit da unten wohl nicht gemacht. Ich hoffe, es nutzt auch was!  Naja, zumindest mir hat´s schon genutzt, ich hab den Stoff jetzt wieder etwas aufgearbeitet...
hummel hat folgendes geschrieben: | Warum können diese Prozeduren nicht aufgerufen werden?
Ansonsten begibt essich in eine Endlosschleife und stürzt ab. |
Das liegt daran, dass dein Code vor Fehlern und Unsauberkeiten geradezu strotzt.  Nicht böse sein, ich denke, ich habe mir die "Predigt" mit der Arbeit, deinen Code zu korrigieren, durchaus verdient, oder?
Also, zur Sache, zunächst mal allgemeine "Anmerkungen":
- Dein Code ist nicht besonders "brauchbar" formatiert, das erschwert das Lesen; schau dich doch mal zum Stichwort "Styleguide" um, wie man Code "besser" aufbereiten kann; ich habe trotzdem - mehr oder weniger - versucht, mich an deine Formatierungsweise zu halten. Naja...
- Du hast Halt; verwendet, um das Programm zu beenden!
Das ist praktisch immer ein Fehler. Besser ist Application.Terminate;!
- Du solltest den Objekten (Buttons, Eingabefelder, etc.) einen aussagekräftigen Namen geben, damit man auch im Code schon sieht, was hier passieren soll.
- Die Variable wahl: Char; habe ich rausgenommen, hat in deinem Code keine Funktion. Statt dessen habe ich das Verhalten der Eingabeprozedur umgestellt, s.u.
- Die Listenköpfe werden nicht initialisiert (bei Programmstart auf NIL gesetzt)! Das ist unter anderem ein Grund für den "Absturz"!
- Du verwendet FloatToStr für Integer-Werte. Dafür ist IntToStr da, noch besser ist StrToIntDef, schau mal in die OH.
- Wenn man mit Zeigern hantiert, ist es immer eine gute Idee, eine separate Liste aller erzeugten Objekte zu führen, damit man beim Programmende keine Speicherlöcher hat. So kann man "gefahrlos" mit den Zeigern "spielen", es können auf diese Weise keine Heapobjekte "verloren" gehen. Ich habe das über AlleElemente: TList; gelöst (das stellt praktisch eine eigene, klitzekleine Speicherverwaltung dar).
- Deine Ausgabeprozeduren sind rekursiv. Das ist zwar ganz nett und sieht auch leidlich elegant aus, aber an dieser Stelle total "oversized"! Ich habe das deshalb auf eine iterative Prozedur umgestellt (auf einer Schleife basierend), die dafür aber durch Parameter beide Listen ausgeben kann.
- Du hast eine Ereignisprozedur für das Sortieren UND eine Sortierprozedur. Wozu? Aber da liegt sowieso der Hase im Pfeffer begraben, s.u.
- Du solltest im OnCreate-Ereignis der Form notwendige Variableninitialisierungen machen und im OnDestroy ggfs. fällige Freigaben (siehe mein Code).
Hier mal deine Eingabeprozedur:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18:
| PROCEDURE TForm1.EINGABE; VAR NEU:ZEIGER; BEGIN wahl:='n'; NEW(KOPFN); KOPFN^.NEXTN:=NIL; KOPFN^.NEXTE:=NIL; KOPFN^.NAME:=edit1.text; KOPFN^.EINKOMMEN:=strtoint(edit2.text); WHILE UPCASE(WAHL) <>'N' DO BEGIN NEW(NEU); KOPFN^.NAME:=edit1.text; KOPFN^.EINKOMMEN:=strtoint(edit2.text); SORTE(NEU); SORTN(NEU); END; END; |
Probleme:
- Da die Eingabe mehrfach aufrufbar ist (per Button), darf nicht KOPFN für den ersten New() verwendet werden, sonst ist die gesamte bisherige Liste "weg"!
- Die while-Schleife wird nie ausgeführt, weil "wahl" immer "n" ist, denn "wahl" wird nie ein Wert zugewiesen (in der Schleife)...
- Aber selbst wenn die while-Schleife ausgeführt würde, ist sie fehlerhaft, denn es wird per New(NEU) ein Element erzeugt, aber die Werte werden an KOPFN und nicht an NEU zugewiesen; ausserdem wird das neue Element gar nicht in die Liste eingehängt...
Für den "Lerneffekt" deshalb hier nochmal meine Eingabeprozedur:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
| procedure TForm1.BtnInputClick(Sender: TObject); var NEU: ZEIGER; begin New(NEU); NEU^.NAME := EName.Text; NEU^.EINKOMMEN := StrToIntDef(EMoney.Text,0); NEU^.NEXTN := KOPFN; KOPFN := NEU; NEU^.NEXTE := KOPFE; KOPFE := NEU; AlleElemente.Add(NEU); AUSGABE(KOPFN,TRUE,LBName); AUSGABE(KOPFE,FALSE,LBMoney); end; |
Erläuterung ggü. deinem Code:
- Es werden mit jedem Klick auf den Button einzelne Elemente aus den aktuellen Editfeldwerten erstellt und an den Anfang der Liste gestellt.
- Ich verwende StrToIntDef, um die Zahl in einen Wert zu verwandeln (als Beispiel); Effekt dabei ist, wenn die eingegebene Zeichenkette keine gültige Zahl darstellt, gibt es keinen Fehler, sondern es wird ein Standard-Wert verwendet.
- Neue Elemente werden in die "Speicherverwaltungs-Liste" eingefügt, damit nix verloren gehen kann (eigentlich nur eine Sicherheitsmaßnahme).
- Es wird nicht sofort sortiert, sondern erst per Button; macht auch irgenwie mehr Sinn so
Jetzt noch zu deiner "Sortierprozedur":
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| PROCEDURE SORTN(NEU:ZEIGER); VAR Z,ZNFG:ZEIGER; GEFUNDEN:BOOLEAN; BEGIN ZNFG:=KOPFN; GEFUNDEN:=FALSE; WHILE (ZNFG<>NIL) AND NOT GEFUNDEN DO BEGIN IF ZNFG^.NAME >NEU^.NAME THEN GEFUNDEN:=TRUE ELSE BEGIN Z:=ZNFG; ZNFG:=ZNFG^.NEXTN; END; END; IF ZNFG=KOPFN THEN BEGIN NEU^.NEXTN:=KOPFN; KOPFN:=NEU; END ELSE BEGIN NEU^.NEXTN:=Z^.NEXTN; Z^.NEXTN:=NEU; END; END; |
Probleme:
- Das ist kein Sortieralgorithmus; hier wird nur ein einziger "Tauschlauf" gemacht (wenn überhaupt), aber nicht, bis die Liste sortiert ist! Korrekt wäre es (in Umgangssprache): gehe die Liste durch und tausche benachbarte Elemente, wenn diese nicht in der richtigen Reihenfolge stehen aus, bis ein kompletter Lauf auftritt, in dem nichts getauscht wurde. -> BubbleSort für lineare Listen.
- ehrlich gesagt weiß ich nicht, was dieser Code da oben überhaupt tut oder wozu das gut ist...
- da KOPFN nicht initialisiert wird, ist das Verhalten der Schleife undefiniert, wenn man direkt nach dem Programmstart auf einen Sortier-Button klickt, da unbekannt ist, was in KOPFN steht -> Grund 1 für deinen "Absturz"
- selbst wenn KOPFN initialisiert ist (per Eingabe-Prozedur; das immerhin leistet diese ja), ist es aber eine potentielle Endlosschleife, weil das Incrementieren der Pointervariablen ZNFG (soll wohl Z-Nachfolger heißen?) nur bedingt ausgeführt wird und nicht immer, was korrekt wäre! -> Grund 2 für deinen "Absturz"
Hier also dann die "korrekte" Sortierprozedur:
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:
| procedure TForm1.BtnSortNameClick(Sender: TObject); var AKTUELL, VORHER: ZEIGER; GETAUSCHT: Boolean; begin if (KOPFN <> NIL) then begin repeat GETAUSCHT := FALSE; VORHER := NIL; AKTUELL := KOPFN; while (AKTUELL^.NEXTN <> NIL) do begin if (AKTUELL^.NAME > AKTUELL^.NEXTN^.NAME) then begin if (VORHER = NIL) then KOPFN := AKTUELL^.NEXTN else VORHER^.NEXTN := AKTUELL^.NEXTN; VORHER := AKTUELL^.NEXTN; AKTUELL^.NEXTN := AKTUELL^.NEXTN^.NEXTN; VORHER^.NEXTN := AKTUELL; GETAUSCHT := TRUE; end else begin VORHER := AKTUELL; AKTUELL := AKTUELL^.NEXTN; end; end; until (NOT GETAUSCHT); end; AUSGABE(KOPFN,TRUE,LBName); end; |
Da das Sortieren einer linearen Liste keine ganz triviale Angelegenheit ist (eigentlich schon, aber man vertut sich permanent mit den doofen Zeigern!  ), habe ich noch ein kleines Demo-Programm geschrieben, was das Sortieren verdeutlichen soll. Einfach mal reinsehen und durchklicken...
cu
Narses
PS: Schönen Gruß an deinen Info-Lehrer.  Er soll sich mal ein bischen mehr anstrengen (oder besser du?  )!
Einloggen, um Attachments anzusehen!
|
|
|