Autor |
Beitrag |
Martello
      
Beiträge: 131
WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
|
Verfasst: Mo 07.06.10 21:12
Hab mal eine Anfänger-Frage zu new/setlenght:
Der folgende Code führt zu einer Zugriffsverletzung an Adresse 0000000: "Im Projekt maxarray.exe ist eine Exception der Klasse EAccessViolation aufgetreten". Offenbar stimmt etwas mit der Zeigerer nicht: Kann mir jemand sagen, was falsch sein könnte? Es sind eigentlich nur erste Vorversuche....
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:
| unit maxarr;
interface
uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls;
type TForm1 = class(TForm) Button1: TButton; Edit1: TEdit; procedure Button1Click(Sender: TObject); private public end;
TListe =^TElement; TElement = record Key: integer; NextElement: TListe; end;
maxa = Array [0..255,0..255] of TListe;
var Form1: TForm1; max: maxa;
implementation
{$R *.dfm}
procedure TForm1.Button1Click(Sender: TObject); var i: integer; zeiger:TListe; begin
new(zeiger); zeiger.NextElement:=nil;
for i:=1 to 255 do begin
max[i,i].key := i; max[i,i].NextElement := zeiger;
end; end;
end. |
Moderiert von Gausi: Code- durch Delphi-Tags ersetzt
Moderiert von Narses: Titel geändert, war: "setlenght ?".
|
|
Tilo
      
Beiträge: 1098
Erhaltene Danke: 13
Win7 geg. WInXP oder sogar Win98
Rad2007
|
Verfasst: Mo 07.06.10 21:33
Frage: Tritt der Fehler in Zeile 47 auf?
Ich bin mit Records in Verbindung mit Arrays im Moment nicht sattelfest. Ich bin der Meinnung es könnte daran liegen dass das Feld zwar existiert der Compiler aber nicht automatisch Die Records im array anlegt und Du deshalb versucht auf ein nicht existierendes Objekt zuzugreifen (EAccessViolation).
Beste Grüße,
Tilo
|
|
Martello 
      
Beiträge: 131
WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
|
Verfasst: Mo 07.06.10 22:14
Tilo hat folgendes geschrieben : | Frage: Tritt der Fehler in Zeile 47 auf? |
Genau in Zeile 47.
Das würde ja bedeuten, ich müsste alle 256*256 Records selbst "anlegen". Aber ich dachte, das passiert, indem ich die Variable vereinbare:
var
max: maxa;
Hat denn ein "Array ohne Inhalt" überhaupt einen Sinn? Oder anders gefragt: Wie müsste ich denn die Records anlegen?
Grüße
M.
|
|
Tilo
      
Beiträge: 1098
Erhaltene Danke: 13
Win7 geg. WInXP oder sogar Win98
Rad2007
|
Verfasst: Mo 07.06.10 22:58
Jetzt sehe die Ursache Dein maxa ist vom Typ Array[,] of TListe, Tliste ist als ^TElement definiert.
Du legst Dir also ein Array voller Pointer an. Die Zellen deines arrays sind zwar mit Werte befüllt. Diese Werte zeigen aber auf einen "zufälligen" Bereich im Speicher.
Du könntest dein maxa eventuell als Array[,] of TElement definieren aber das befreit Dich dennoch nicht Das Array zu initialisieren. Jedes Deines Felder würde einen zufälligen Wert für Key und NextElement aufweisen. Dies birgt ebenfalls die Gefahr einer Zugriffsverletzung. Zwar nicht beim ersten Befüllen da Du da die ungültigen Werte überschreibst. Sobald Du aber lesend auf ein nicht vor beschriebenes Element im Array zugreifst kommt es zu einer Zugriffsverletzung.
Einziger Schutz: Initialisierung auf bestimmte Werte. Bei Key musst Du sehen ob es einen Wert gibt der von einem gültigen Key nie angenommen wird => prüfen. Für Pointer hat sich in Pascal an "nil"* als Symbol für einen ungültigen Pointer eingebürgert.
*nil => Not In List
|
|
Martello 
      
Beiträge: 131
WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
|
Verfasst: Di 08.06.10 10:40
Tilo hat folgendes geschrieben : | Jetzt sehe die Ursache Dein maxa ist vom Typ Array[,] of TListe, Tliste ist als ^TElement definiert...
Einziger Schutz: Initialisierung auf bestimmte Werte. |
Da sehr viele Elemente leer bleiben werden, ging ich davon aus, das ganze Feld mit einem einzigen Zeiger (immer dem gleichen) initialisieren zu können. Und erst beim Programmablauf würden dann manche Felder mit sinnvollen Daten gefüllt werden.
Aber offensichtlich muss ich das ganze Feld zuerst Knoten für Knoten initialisieren. Ich werde also ganz vorne im Programm alle 256*256 Array-Elemente mit je einem Key und mit je einem Zeiger belegen müssen. Damit kollidiere ich zwar mit der Idee, mit einem einzigen Zeiger auszukommen. Aber Speicherprobleme sollten trotzdem nicht so schnell auftreten.
|
|
Martello 
      
Beiträge: 131
WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
|
Verfasst: Di 08.06.10 21:52
Tilo hat folgendes geschrieben : |
Einziger Schutz: Initialisierung auf bestimmte Werte. Bei Key musst Du sehen ob es einen Wert gibt der von einem gültigen Key nie angenommen wird => prüfen. Für Pointer hat sich in Pascal an "nil"* als Symbol für einen ungültigen Pointer eingebürgert. |
Ich habe folgendes probiert:
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19:
| procedure TForm1.Button1Click(Sender: TObject); var i,j: integer; zeiger:TListe;
begin
for i:=0 to 255 do begin for j:=0 to 255 do begin
max[i,j].key := 1; new(zeiger); zeiger.NextElement:=nil; max[i,j].NextElement := zeiger;
end; end; end;
end. |
Aber die Fehlermeldung kommt immer noch
Gibt es andere Möglichkeiten der Initialisierung?
Gruß,
M.
|
|
Tilo
      
Beiträge: 1098
Erhaltene Danke: 13
Win7 geg. WInXP oder sogar Win98
Rad2007
|
Verfasst: Di 08.06.10 22:58
Dein Denkfehler ist, das Du den Element deines Feldes Werte zuweisen möchtest bevor Du die Elemente des Feldes überhaupt erzeugt hast (Annahme: Felder sind vom Typ TListe).
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:
| procedure TForm1.Button1Click(Sender: TObject); var i,j: integer; zeiger:TListe;
begin for i:=0 to 255 do begin for j:=0 to 255 do begin zeiger=new(Tliste); zeiger.key:=1; zeiger.NextElement:=nil; max[i,j]:=zeiger; end; end;
new(zeiger); zeiger.NextElement:=nil; for i:=1 to 255 do begin max[i,i].key := i; max[i,i].NextElement := zeiger; end; |
Dein Code aus dem Anfangsbeitrag war korrekt bis auf den Fehler, das Dein Feld nicht initialisiert war.
end;
|
|
Martello 
      
Beiträge: 131
WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
|
Verfasst: Mi 09.06.10 21:56
Tilo hat folgendes geschrieben : |
Delphi-Quelltext 1: 2: 3: 4: 5: 6: 7: 8:
| new(zeiger); zeiger.NextElement:=nil; for i:=1 to 255 do begin max[i,i].key := i; max[i,i].NextElement := ZEIGER; end; |
Dein Code aus dem Anfangsbeitrag war korrekt bis auf den Fehler, das Dein Feld nicht initialisiert war.
|
Der groß geschriebene ZEIGER war an dieser Stelle zwar existent, aber nicht initialisiert? Verstehe ich das richtig?
Oder ist auch schon die Zuweisung max[i,i].key := i; inkorrekt?
Es ist fast ein lebenslängliches Problem(chen:-). Vor ca. 30 Jahren hatten wir im Ingenieurstudium Pascal 2 Semester lang zu Studienbeginn. Obwohl es vorgesehen war, schaffte der Dozent die Sache mit den Pointern nicht mehr... Danach habe ich es - Schande über mein Haupt - einfach nie richtig gelernt. Mit umso mehr Mühe muss ich das nun nachholen.
Aber wie sagte das berühmte Geburtstagskind von gestern vor ca. 200 - x Jahren in seinen Haus- und Lebensregeln:
"Es ist des Lernens kein Ende."
Vielen Dank für die Hilfestellung hierbei 
|
|
Tilo
      
Beiträge: 1098
Erhaltene Danke: 13
Win7 geg. WInXP oder sogar Win98
Rad2007
|
Verfasst: Do 10.06.10 00:56
Der ZEIGER war von Dir korrekt erzeugt und mit Werten belegt worden. Nicht exsistent war das "Objekt" auf den max[i,i] zeigt. Du definierst ein Array dessen Feldelemente vom Typ TListe sind. TListe ist bei Dir ein Pointer. Das heißt BEVOR Du die Feldelementen verwenden kannst (nichts anderes ist "max[i,i].NextElement:=ZEIGER") must Du diese erst erzeugen. Dein Code an sich ist korrekt und hätte funktioniert wenn irgenwo im Programm (z.B. im OnCreate der Form) das in Button1Click verwendete Array initialisiert worden wäre. Merke: Bei einen Array of Pointer legt Delphi eine Array-Struktur mit Pointer an, die an irgend eine Stelle zielen und nicht auf gültige Objekte verweisen.
|
|
Martello 
      
Beiträge: 131
WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
|
Verfasst: Do 10.06.10 10:57
Tilo hat folgendes geschrieben : | Merke: Bei einen Array of Pointer legt Delphi eine Array-Struktur mit Pointer an, die an irgend eine Stelle zielen und nicht auf gültige Objekte verweisen. |
Ich glaube, so nach und nach verstehe ich: Bei der Verabredung "var max : Maxa;" wird zwar Speicherplatz für 256*256 Stück Records reserviert, aber an den Stellen der Zeiger steht dort natürlich vollkommen Undefiniertes. Die zeigen zwar alle irgendwo hin, aber diese Bereiche dürfen natürlich aus systembedingten Gründen nicht angerührt werden. Also muss man zuerst gültige Bereiche erzeugen (initialisieren), auf die diese Zeiger dann zeigen dürfen, ohne Schaden anzurichten.
M.
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Do 10.06.10 12:12
Martello hat folgendes geschrieben : | Ich glaube, so nach und nach verstehe ich: Bei der Verabredung "var max : Maxa;" wird zwar Speicherplatz für 256*256 Stück Records reserviert |
Nein. Es wird Speicher für 256^2 mal TZeiger reserviert. Sozusagen 256^2*4byte.
Danach hast du ein Feld mit 256^2 undefinierten Zeigern, die irgendwo hin zeigen.
Martello hat folgendes geschrieben : | Also muss man zuerst gültige Bereiche erzeugen (initialisieren), auf die diese Zeiger dann zeigen dürfen, ohne Schaden anzurichten. |
Genau.
Jedes Feld muss also mit einem New(TZeiger) initialisiert werden.
Martello hat folgendes geschrieben : | Da sehr viele Elemente leer bleiben werden, ging ich davon aus, das ganze Feld mit einem einzigen Zeiger (immer dem gleichen) initialisieren zu können. Und erst beim Programmablauf würden dann manche Felder mit sinnvollen Daten gefüllt werden. |
Dann würde es sich doch anbieten, an den ungültigen Stellen "here be dragons" ääähm NIL reinzuschrieben. Immerhin sagt dieser Wert ja genau das aus: dieser Zeiger ist ungültig.
Da NIL dem numerischen Wert 0 entspricht, kannst du folgendes tun:
Delphi-Quelltext 1: 2: 3: 4:
| var max: maxa; begin FillChar(max,sizeof(max),0); ... |
Was FillChar macht, verrät die Delphi-Hilfe 
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
Martello 
      
Beiträge: 131
WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
|
Verfasst: Do 10.06.10 21:59
Zitat: |
Was FillChar macht, verrät die Delphi-Hilfe  |
Wie kann man eigentlich "Entwürfe" posten? Bei "Absenden" aus meiner Entwurfsecke landet es immer wieder dort, aber nicht hier... 
|
|
Xentar
      
Beiträge: 2077
Erhaltene Danke: 2
Win XP
Delphi 5 Ent., Delphi 2007 Prof
|
Verfasst: Do 10.06.10 22:09
Ich denk mal, das dient nur als "Lager", damit man die schneller in ne Antwort copy-pasten kann?
Das ganze geht aber ein wenig hier am Thema vorbei 
_________________ PROGRAMMER: A device for converting coffee into software.
|
|
Martello 
      
Beiträge: 131
WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
|
Verfasst: Do 10.06.10 23:09
|
|
Martok
      
Beiträge: 3661
Erhaltene Danke: 604
Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
|
Verfasst: Do 10.06.10 23:19
_________________ "The phoenix's price isn't inevitable. It's not part of some deep balance built into the universe. It's just the parts of the game where you haven't figured out yet how to cheat."
|
|
Martello 
      
Beiträge: 131
WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
|
Verfasst: Do 10.06.10 23:36
|
|
Martello 
      
Beiträge: 131
WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
|
Verfasst: Do 10.06.10 23:48
Martok hat folgendes geschrieben : | Hm. Das heißt, du musst sowieso an JEDES Feld Daten dranhängen? Dann bringt doch der Aufwand nix, und du kannst direkt ohne Pointer arbeiten. Versteh grad was Problem wohl nicht. |
Die Matrix spannt ein Koordinatengitter über ein Gelände. Einige Felder werden mit Objekten gefüllt, die meisten bleiben leer. Das Programm soll jeweils alle Objekte in einem Quadrat aus 9 Teilfeldern (ähnlich einem Sudoku-Feld) in eine Liste schreiben. Und beim Programmstart sollen daher jeweils mehrere Objekte, die zufällig in das selbe Feld fallen, als lineare Liste verkettet werden.
In Turbo Pascal ist das die einzige Möglichkeit, bei unbekannter Anzahl der möglichen Objekte pro Feld, eine solche Datenstruktur aufzubauen (dreidimensionale Struktur mit offenem Ende der z-Komponente). Aber am Ende geht das in Delphi alles viel einfacher, mit Datentypen/Konstrukten, die ich möglicherweise nur nicht kenne?
M.
|
|
|