Entwickler-Ecke
Delphi Language (Object-Pascal) / CLX - Problem mit Pointern
FatalError - Sa 06.12.03 15:39
Titel: Problem mit Pointern
Hi,
ich hab grad ein problem mit Zeigern, im Programm (siehe unten) kann man zwar in der Wurzel(anf) und unter anderen Zeogern noch weitere Daten speichern, jedoch kann ich nur die Wurzel wieder auslesen, beim Aufrufen anderer Daten aus den Zeigern kommt immer eine Fehlermeldung ‚access Violation’ an der unten markierten Stelle. Woran kann das liegen?
Die Daten sollten theoretisch alle in einem binären Baum stehen.
p.s. bin ich mit dieser Frage hier richtig?
Gruß
andy
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: 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: 153: 154: 155: 156: 157: 158:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls;
type pointer=^Element; Element = record nachname,vorname,telefon,handy,wohnort,hausnummer: string; l,r: pointer end; TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; Edit4: TEdit; Edit5: TEdit; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; Edit6: TEdit; Label6: TLabel; Label7: TLabel; Label8: TLabel; Button1: TButton; Button2: TButton; Button3: TButton; Button4: TButton; Button5: TButton; Button6: TButton; Label9: TLabel; Label10: TLabel; Panel1: TPanel; Button7: TButton; Button8: TButton; procedure Button6Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button7Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button8Click(Sender: TObject); private public end;
var Form1: TForm1; anf, hilf: pointer; anz: integer;
implementation
{$R *.dfm}
procedure TForm1.Button6Click(Sender: TObject); begin close end;
procedure TForm1.FormCreate(Sender: TObject); begin Form1.Caption:='Telefonbuch v.1.0'; anz:= 0; end;
procedure TForm1.Button3Click(Sender: TObject); procedure einfuegen; begin if anf= nil then begin new(anf); new(hilf); anf^.nachname := Edit1.Text; anf^.vorname := Edit2.Text; anf^.telefon := Edit3.Text; anf^.handy := Edit4.Text; anf^.wohnort := Edit5.Text; anf^.hausnummer:= Edit6.Text; anf^.l:= nil; anf^.r:= nil; end else begin hilf:=anf; while (hilf<>nil) and (hilf^.nachname<>edit1.text)do begin if edit1.text<hilf^.nachname then hilf:= hilf^.l else hilf:=hilf^.r; end; if (hilf <>nil) and (hilf^.nachname=edit1.text) then Edit2.Text:='Gibt es schon.' else begin new(hilf); hilf^.nachname := Edit1.text; hilf^.vorname := Edit2.Text; hilf^.telefon := Edit3.Text; hilf^.handy := Edit4.Text; hilf^.wohnort := Edit5.Text; hilf^.hausnummer:= Edit6.Text; hilf^.l:= nil; hilf^.r:= nil; end; end;
end;
begin einfuegen; end;
procedure TForm1.Button7Click(Sender: TObject); begin Edit1.text:=''; Edit2.text:=''; Edit3.text:=''; Edit4.text:=''; Edit5.text:=''; Edit6.text:=''; end;
procedure TForm1.Button4Click(Sender: TObject); procedure suchen;
begin begin hilf:=anf; while (hilf<> nil) and (hilf^.nachname<> Edit1.Text) do begin if Edit1.Text<hilf^.nachname then hilf:= hilf^.l else hilf:= hilf^.r; end; if hilf^.nachname = Edit1.Text then begin Edit2.text:= hilf^.vorname; Edit3.text:= hilf^.telefon; Edit4.text:= hilf^.handy; Edit5.text:= hilf^.wohnort; Edit6.text:= hilf^.hausnummer; end; if hilf= nil then edit2.Text:='Not in list.'
end;
end;
begin suchen; end;
end. |
Moderiert von
Klabautermann: Quote- durch Delphi-Tags ersetzt.
Nightmare_82 - Mo 08.12.03 00:51
beim Button4Click weist du hilf hilf.r bzw. hilf.l zu. Das machst du solagne hilf<> nil ist oder der Name gefunden wurde.
Wenn aber die erste Bedingung eintrifft(while hilf<> nil), ist hilf danach auf jeden fall nil. Du greifst aber genau nach der While-Shleife auf diesen Null-Pointer zu...
du mußt direkt nach der While-Schleife folgendes schreiben:
Delphi-Quelltext
1: 2: 3: 4: 5:
| if hilf= nil then begin edit2.Text:='Not in list.' exit; end; |
Delete - Mo 08.12.03 03:35
Ein Dispose nach einem new wäre auch keine schlechte Idee. :wink:
Motzi - Mo 08.12.03 17:53
Ich hab mir jetzt mal die Mühe gemacht deinen Code ein bisschen umzuformatieren:
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: 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: 153: 154: 155: 156: 157: 158: 159: 160: 161: 162: 163:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls;
type PElement = ^TElement; TElement = record nachname, vorname, telefon, handy, wohnort, hausnummer: string; l, r: PElement; end;
TForm1 = class(TForm) Edit1: TEdit; Edit2: TEdit; Edit3: TEdit; Edit4: TEdit; Edit5: TEdit; Label1: TLabel; Label2: TLabel; Label3: TLabel; Label4: TLabel; Edit6: TEdit; Label6: TLabel; Label7: TLabel; Label8: TLabel; Button1: TButton; Button2: TButton; Button3: TButton; Button4: TButton; Button5: TButton; Button6: TButton; Label9: TLabel; Label10: TLabel; Panel1: TPanel; Button7: TButton; Button8: TButton; procedure Button6Click(Sender: TObject); procedure FormCreate(Sender: TObject); procedure Button3Click(Sender: TObject); procedure Button7Click(Sender: TObject); procedure Button4Click(Sender: TObject); procedure Button8Click(Sender: TObject); private public end;
var Form1: TForm1; anf, hilf: PElement; anz: integer;
implementation
{$R *.dfm}
procedure TForm1.Button6Click(Sender: TObject); begin Close; end;
procedure TForm1.FormCreate(Sender: TObject); begin Form1.Caption := 'Telefonbuch v.1.0'; anz := 0; end;
procedure TForm1.Button3Click(Sender: TObject);
procedure einfuegen; begin if anf = nil then begin new(anf); new(hilf); anf^.nachname := Edit1.Text; anf^.vorname := Edit2.Text; anf^.telefon := Edit3.Text; anf^.handy := Edit4.Text; anf^.wohnort := Edit5.Text; anf^.hausnummer := Edit6.Text; anf^.l := nil; anf^.r := nil; end else begin hilf := anf; while (hilf <> nil) and (hilf^.nachname <> edit1.text)do begin if edit1.text < hilf^.nachname then hilf := hilf^.l else hilf := hilf^.r; end;
if (hilf <> nil) and (hilf^.nachname = edit1.text) then Edit2.Text := 'Gibt es schon.' else begin new(hilf); hilf^.nachname := Edit1.text; hilf^.vorname := Edit2.Text; hilf^.telefon := Edit3.Text; hilf^.handy := Edit4.Text; hilf^.wohnort := Edit5.Text; hilf^.hausnumme r:= Edit6.Text; hilf^.l := nil; hilf^.r := nil; end; end; end;
begin einfuegen; end;
procedure TForm1.Button7Click(Sender: TObject); begin Edit1.text := ''; Edit2.text := ''; Edit3.text := ''; Edit4.text := ''; Edit5.text := ''; Edit6.text := ''; end;
procedure TForm1.Button4Click(Sender: TObject);
procedure suchen; begin hilf := anf; while (hilf <> nil) and (hilf^.nachname <> Edit1.Text) do begin if Edit1.Text < hilf^.nachname then hilf := hilf^.l else hilf := hilf^.r; end;
if hilf^.nachname = Edit1.Text then begin Edit2.text := hilf^.vorname; Edit3.text := hilf^.telefon; Edit4.text := hilf^.handy; Edit5.text := hilf^.wohnort; Edit6.text := hilf^.hausnummer; end;
if hilf = nil then edit2.Text := 'Not in list.' end;
begin suchen; end;
end. |
Ein gut formatierter Sourcecode erleichter schonmal einiges, das zweite wichtige sind gut gewählte Variablen-Namen! Du hast in deinem Code den Record und den Pointer auf diesen folgendermaßen deklariert:
Delphi-Quelltext
1: 2: 3: 4: 5:
| pointer=^Element; Element = record nachname,vorname,telefon,handy,wohnort,hausnummer: string; l,r: pointer end; |
Unter Delphi ist es üblich Records und Klassen mit einem großen "T" (für type) als Präfix zu deklarieren, also nicht "Element" sondern "TElement" (hab ich in meinem formatierten Code ausgebessert). Weiters deklarierst du einen Zeiger auf einen solchen Record mit dem Namen "Pointer", der Name "Pointer" wird in Delphi jedoch schon verwendet, nämlich als "Universal-Zeiger". Einen Pointer auf einen Record deklariert man normalerweise mit einem großen "P" (eben für Pointer) anstatt des großen "T" -> also "PElement" (hab ich auch schon ausgebessert). Die ganzen String-Felder des Records haben ganz vernünftige Namen, aber mit den beiden Felder "l" und "r" kann ich nicht viel anfangen... ich nehme mal stark an, dass diese Felder die Adressen der Elemente "vor" bzw "nach" dem jeweiligen Element enthalten sollten, dies geht jedoch aus den Namen nicht wirklich hervor...
Ich schätze mal, dass das ganze ein Adressbuch werden soll, in dem alle Einträge in einer doppelt verketteten Liste gespeichert werden sollen... allerdings hab ich nirgends eine Zeile gefunden in der die beiden Felder l und r gesetzt werden, in denen die Adressen der vorherigen bzw nachfolgenden Felder gesetzt werden (hab die entsprechende Zeile kommentiert)... Mach dir mal die Funktionsweise einer solchen doppelt verketteten Liste bewusst und gewöhn dir vielleicht eine bessere Formatierung und aussagekräftigere Variablennamen an... am fehlenden Wissen für den Umgang mit Pointern scheitert es IMHO nämlich nicht (von den fehlenden Aufrufe von Dispose mal abgesehen)
FatalError - Mo 08.12.03 21:06
Hi,
erst mal danke für die vielen Antworten und Verbesserungsvorschläge!
Zur Sache:
@Luckie
Was ist ein Dispose? (Verzeih mir die Frage, aber in Delphi bin ich ein totaler Newb;-)
@Motzi
Stimmt, soll ein Telefonbuch werden, in dem beliebig Daten hinzugefügt werden können.
Zu “l“ und „r“:
Das Telefonbuch soll zwcks. Laufzeitoptimierung nach Art eines „binären Baumes strukturiert“ werden, d.h. wie im Beispiel nach Zahlen (siehe Grafik) werden hier die Daten nach dem Nachnamen geordnet (Kriterien kleiner</größer>).
Der Nachname ist das Ordnungskriterium, praktisch die Überschrift für ein jedes Datenpaket, dessen einzelne Teile dann z.B. über hilf^.vorname ausgelesen werden.
„l“ und „r“ zeigen dann immer auf das nächste Datenpaket (den nächsten Knoten) links unten(“l“) oder rechts unten („r“).
„anf“ ist root, also das Paket ganz oben, die Wurzel des Baumes. Bei new(anf) wird der Baum neu erstellt.
Die Blätter ganz unten sind immer „nil“ (not in list), soll dem Programm zeigen, dass der jeweilige Speicherpatz noch frei ist bzw. bei der Suche nach Daten, dass hier der Baum zuende ist und jetzt ein anderer Zweig durchsucht werde muss.
(na gut, bei ein paar Zahlen u. Buchstaben bringen binäre Bäume noch nix; soll nur zeigen, DASS es klappt und eine praktische Übung für ein Programm sein, in dem wesentlich mehr Daten anfallen, dachte an einen Fluid/Gas-simulator in 2D)
ach ja, bzgl. „gut formatierter sourcecode“..
Übung macht den Meister, und ich brauch wohl noch ein wenig davon...;-)
@Nightmare_82
müsste es dann nicht heissen:
while (hilf^.l<> nil) ?
Ziel ist: wenn das nächste hilf links oder rechts nil ist, dann kann dort nichts stehen also geht man in der Suche nach links weiter, bis der ganze Baum durchlaufen ist.
Grüße
Andy
p.s. danke für die Delphi-tags:-)
:D
Wie hängt man ein Bild von der Festplatte an?
Motzi - Mo 08.12.03 21:22
Ja, allerdings sind bei deinem Code sowohl l als auch r in allen Fällen immer nil! Ich finde in deinem Code keine einzige Zeile in denen den beiden Feldern ein anderer Wert als nil eingetragen wird..!
AndyB - Mo 08.12.03 21:32
| FatalError hat folgendes geschrieben: |
| Was ist ein Dispose? |
Da gibt es so eine Taste zwischen ESC und F2. Warum wurde die nur so versteckt auf der Tastatur angebracht?
FatalError - Mo 08.12.03 23:29
Hi motzi,
Hast schon recht, l und r wird nie DIREKT was anderes als nil zugewiesen, kommt allerdings vor, dass ich z.B. hilf:= hilf^.l setze und dann hilf einen Wert zuweise, z.B. hilf^.nachname:= Editblabla.text;
Gruß
andy :D
Motzi - Di 09.12.03 10:26
Ja, aber da l und r NIE etwas anderes als nil enthalten können ist sowas
Delphi-Quelltext
1: 2:
| hilf := hilf^.l; hilf^.nachname := Editblabla.text; |
eine potenzielle Fehlerquelle!
Denn wenn l nil ist und du der Variable hilf l zuweißt ist hilf logischerweiße auch nil! Und wenn du hilf dann dereferenzierst um einem Feld etwas zuzuweisen hagelt es natürlich einen EAccessViolent!
Motzi - Di 09.12.03 11:08
Ich hab dir jetzt mal ein kleines Beispiel-Programm geschrieben, dass ebenfalls eine double-linked-list verwendet. Der Einfachheit halber enthält der Record nur einen String und ist nicht sortiert, aber das Prinzip ist zu erkennen und den Rest kannst du ja dann selber einbaun...
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: 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:
| unit Unit1;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type PElement = ^TElement; TElement = record sName : String; pPrevious : PElement; pNext : PElement; end;
TForm1 = class(TForm) ElementName: TEdit; ListBox1: TListBox; Hinzufuegen: TButton; RefreshList: TButton; procedure HinzufuegenClick(Sender: TObject); procedure RefreshListClick(Sender: TObject); procedure FormDestroy(Sender: TObject); private public pFirst: PElement; end;
var Form1: TForm1;
implementation
{$R *.dfm}
procedure TForm1.HinzufuegenClick(Sender: TObject); var pNew: PElement; begin pNew := GetMemory(SizeOf(TElement)); pNew^.sName := ElementName.Text;
if not Assigned(pFirst) then begin pFirst := pNew; pFirst^.pPrevious := nil; pFirst^.pNext := nil; end else begin pNew^.pPrevious := pFirst; pNew^.pNext := nil;
while Assigned(pNew^.pPrevious^.pNext) do pNew^.pPrevious := pNew^.pPrevious^.pNext;
pNew^.pPrevious^.pNext := pNew; end; end;
procedure TForm1.RefreshListClick(Sender: TObject); var p: PElement; begin Listbox1.Clear; p := pFirst; while Assigned(p) do begin Listbox1.Items.Add(p^.sName); p := p^.pNext; end; end;
procedure TForm1.FormDestroy(Sender: TObject); var p, pTemp: PElement; begin p := pFirst; while Assigned(p) do begin pTemp := p; p := p^.pNext; FreeMemory(pTemp); end; end;
end. |
FatalError - Mi 10.12.03 16:38
Hi,
danke erstmal für das Programm oben, muss mich erstmal da durchkämpfen :lol:
Zu hilf:
Blöde Frage, aber was kann ich hilf denn zuweisen, ausser nil? Strings anscheinend nicht, da bekomme ich automatisch einen Fehler angezeigt!
Achja, kannst du das mit dem Pfeil auslagern evtl. nochmal näher erklären? Die Daten meines Programms sollen dauerhaft gespeichert werden(auch nach beenden des Programms/runterfahren d.C.), daher hab ich mal versucht dein Beispiel auf mein Programm zu übertragen, hat keinen Fehler gegeben, aber auch net gefunzt...
Gruß
Andy :D
Motzi - Mi 10.12.03 18:58
| FatalError hat folgendes geschrieben: |
| danke erstmal für das Programm oben, muss mich erstmal da durchkämpfen :lol: |
Habs etwas optimiert, aber versucht alles soweit wie möglich mit Kommentaren verständlich zu machen...
| Zitat: |
| [Blöde Frage, aber was kann ich hilf denn zuweisen, ausser nil? Strings anscheinend nicht, da bekomme ich automatisch einen Fehler angezeigt! |
Naja, hilf ist ein Pointer, also kannst du ihm auch jeden Pointer zuweisen..! ;) Aber du solltest hilf nicht gobal deklarieren, sondern immer nur als lokale Hilfsvariable - du kannst dir ja anschaun wie ich das in meinem Code gemacht hab...
| Zitat: |
| Achja, kannst du das mit dem Pfeil auslagern evtl. nochmal näher erklären? Die Daten meines Programms sollen dauerhaft gespeichert werden(auch nach beenden des Programms/runterfahren d.C.), daher hab ich mal versucht dein Beispiel auf mein Programm zu übertragen, hat keinen Fehler gegeben, aber auch net gefunzt... |
Was meinst du mit "Pfeil auslagern"? Um eine solche Datei zu speichern musst du dir selber was überlegen... eine eigene Struktur wie die Datei aufgebaut sein soll!
FatalError - Mi 10.12.03 20:33
Hi,
danke für die Optimierung! :D
Zu "auslagern"
Unter der Procedure "FormDestroy"(schließen des Programmfensters?) schreibst du:
| Zitat: |
| Zitat: | | Zeiger temporär speichern |
Bedeutet dass, dass das, was im Poniter gespeichert ist, dann auch nach beenden des Programms gespeichert ist? (Wäre dann das, was ich suche!)
Gruß
Andy :wink: |
Motzi - Do 11.12.03 09:18
Nein... die ganze Zeile schaut so aus:
p und pTemp sind folgendermaßen definiert:
Beides sind also Zeiger! Was passiert nun wenn man einem Zeiger einen Zeiger zuweißt? Es wird
nur die Adresse vom einen Zeiger in den anderen kopiert!
Nicht der "Inhalt" auf den der Zeiger zeigt! Ich benötige diesen temporären Pointer nur um die Adresse auf das eine Element zu speichern und hole mir dann den Pointer auf das nächste Element. Und über den temporären Pointer kann ich das Element dann freigeben...
Fürs langfristige speichern (auf der Festplatte) musst du dir wie gesagt ein eigenes Format überlegen...
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!