Autor Beitrag
Martello
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 131

WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
BeitragVerfasst: 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....


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:
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
    { Private-Deklarationen }
  public
    { Public-Deklarationen }
  end;

 TListe   =^TElement;
 TElement = record
           Key:     integer;
           NextElement: TListe;
           end;

 maxa  = Array [0..255,0..255of 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 user profile iconGausi: Code- durch Delphi-Tags ersetzt
Moderiert von user profile iconNarses: Titel geändert, war: "setlenght ?".
Tilo
ontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic starofftopic star
Beiträge: 1098
Erhaltene Danke: 13

Win7 geg. WInXP oder sogar Win98
Rad2007
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 131

WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
BeitragVerfasst: Mo 07.06.10 22:14 
user profile iconTilo hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic starofftopic star
Beiträge: 1098
Erhaltene Danke: 13

Win7 geg. WInXP oder sogar Win98
Rad2007
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 131

WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
BeitragVerfasst: Di 08.06.10 10:40 
user profile iconTilo hat folgendes geschrieben Zum zitierten Posting springen:
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 131

WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
BeitragVerfasst: Di 08.06.10 21:52 
user profile iconTilo hat folgendes geschrieben Zum zitierten Posting springen:

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:

ausblenden 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 :cry:

Gibt es andere Möglichkeiten der Initialisierung?

Gruß,
M.
Tilo
ontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic starofftopic star
Beiträge: 1098
Erhaltene Danke: 13

Win7 geg. WInXP oder sogar Win98
Rad2007
BeitragVerfasst: 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).


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:
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); //1. Zeiger auf ein neues Objekt von TListe erzeugen und mit "zeiger" verbinden.
    zeiger.key:=1//2.1. Anfangswerte setzen für Key
    zeiger.NextElement:=nil//2.2 Anfangswerte setzen für NextElement
    max[i,j]:=zeiger; //3. Der Zelle an Position[i,j] im Feld ein gültiges Objekt zuweisen
  end;
end;
//Feld an sich ist initialisiert und kann verwendet werden

//Code aus 1. Post
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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 131

WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
BeitragVerfasst: Mi 09.06.10 21:56 
user profile iconTilo hat folgendes geschrieben Zum zitierten Posting springen:


ausblenden Delphi-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
//Code aus 1. Post
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
ontopic starontopic starontopic starontopic starofftopic starofftopic starofftopic starofftopic star
Beiträge: 1098
Erhaltene Danke: 13

Win7 geg. WInXP oder sogar Win98
Rad2007
BeitragVerfasst: 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 131

WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
BeitragVerfasst: Do 10.06.10 10:57 
user profile iconTilo hat folgendes geschrieben Zum zitierten Posting springen:
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
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Do 10.06.10 12:12 
user profile iconMartello hat folgendes geschrieben Zum zitierten Posting springen:
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.

user profile iconMartello hat folgendes geschrieben Zum zitierten Posting springen:
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.

user profile iconMartello hat folgendes geschrieben Zum zitierten Posting springen:
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:

ausblenden 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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 131

WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
BeitragVerfasst: 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... :x
Xentar
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starofftopic star
Beiträge: 2077
Erhaltene Danke: 2

Win XP
Delphi 5 Ent., Delphi 2007 Prof
BeitragVerfasst: 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 :D

_________________
PROGRAMMER: A device for converting coffee into software.
Martello Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 131

WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
BeitragVerfasst: Do 10.06.10 23:09 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:

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:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
var max: maxa;
begin
  FillChar(max,sizeof(max),0);
...


Was FillChar macht, verrät die Delphi-Hilfe ;)


Ich vermute mal, dass FillChar die kleine Schwester von FillWord ist :wink:

"Ungültige Stellen" wird es keine geben; die meisten werden nur nie mit Daten gefüllt werden, also leer bleiben. Allerdings brauche ich später nur gültige Felder, denn es wird auch auf leere Felder zugegriffen werden - und dabei sollten möglichst keine Verletzungen aufteten.

Wäre max ein array of Element und nicht of ^Element wäre alles soo viel einfacher. Ich weiß schon, warum ich Zeiger so lange gemieden habe :?
M.
Martok
ontopic starontopic starontopic starontopic starontopic starontopic starofftopic starofftopic star
Beiträge: 3661
Erhaltene Danke: 604

Win 8.1, Win 10 x64
Pascal: Lazarus Snapshot, Delphi 7,2007; PHP, JS: WebStorm
BeitragVerfasst: Do 10.06.10 23:19 
user profile iconMartello hat folgendes geschrieben Zum zitierten Posting springen:
Wie kann man eigentlich "Entwürfe" posten? Bei "Absenden" aus meiner Entwurfsecke landet es immer wieder dort, aber nicht hier... :x

Über dem Editor gibts eine Button-Leiste "Entwürfe". Da kannst du die dann auch einfügen.

user profile iconMartello hat folgendes geschrieben Zum zitierten Posting springen:
Ich vermute mal, dass FillChar die kleine Schwester von FillWord ist :wink:

Eher die Mutter, aber ja ;)

user profile iconMartello hat folgendes geschrieben Zum zitierten Posting springen:
"Ungültige Stellen" wird es keine geben; die meisten werden nur nie mit Daten gefüllt werden, also leer bleiben. Allerdings brauche ich später nur gültige Felder, denn es wird auch auf leere Felder zugegriffen werden - und dabei sollten möglichst keine Verletzungen aufteten.

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.

_________________
"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 Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 131

WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
BeitragVerfasst: Do 10.06.10 23:36 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:

ausblenden Delphi-Quelltext
1:
2:
3:
4:
var max: maxa;
begin
  FillChar(max,sizeof(max),0);
...



Ich hab mal ein wenig rumprobiert:

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:
var
  Form1: TForm1;
  max: maxa;

implementation

{$R *.dfm}

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

  //FillChar(max,sizeof(max),0);
  zeiger:=new(TListe);
  zeiger.key:= i;
  zeiger.NextElement:=nil;

max[i,j]:= zeiger;
edit1.Text:=inttostr(max[i,j].key);
                      end;
                    end;
end;

end.


Mit FillChar erzeuge ich doch nur eine Folge von NIL-Zeigern. Aber Initialisierung bedeutet doch, später nutzbare Record-Bereiche herstellen, auf die diese Zeiger dann auch zeigen dürfen. Inwiefern wäre da FillChar sinnvoll? Mir gelingt es jedenfalls nicht, durch Verwendung der FillChar-Zeile mein Test-Programm zum Laufen zu bringen.
M.
Martello Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 131

WIN 2000
TP, C, Delphi 1.0, Turbo Delphi 2006, Delphi 7
BeitragVerfasst: Do 10.06.10 23:48 
user profile iconMartok hat folgendes geschrieben Zum zitierten Posting springen:
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? :oops:
M.