[GELÖST] Dynamisches Array - Exception SIGSEGV bei setLength

Für Fragen von Einsteigern und Programmieranfängern...
tightTannic
Beiträge: 20
Registriert: Di 31. Jan 2017, 19:46
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit
Wohnort: Berlin

[GELÖST] Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von tightTannic »

Guten Tag,
ich hab mal wieder ein Problem mit arrays, leider helfen mir die unzähligen im-Netz-bereits-existierenden Hilfeschreie anderer Progger nicht weiter...
also dachte ich mir, wie man in den Wald ruft, so schallt es heraus :lol:

Die Anwendung soll ein kleines 2D-Spiel werden, wobei es eine 2-dimensionale Map gibt (vergleichbar mit Schiffeversenken, Schach etc.). Diese soll mit einem dynamischen Array (Cluster) realisiert werden, welches sich in einer separaten Unit (unit_data) in der Klasse data:Tdata befindet (dynamisches array, weil der Spieler die Dimensionen der Karte verändern kann). Tfield=class sind die einzelnen Elemente die dem array zugewiesen werden und befinden sich auch in der unit_data.

Code: Alles auswählen

 Tdata = class
  private
    Cluster:array of array of TField; // die map, bestehend aus x*y TFields
  public
    procedure setCluster_dimensions(x,y:integer)// um dem dyn. array die Länge zuzuweisen
    function getField(i,j:integer):Tfield; // gibt ein Element des dyn. arrays aus (mit dem index cluster[i,j])
    procedure setField(i,j:integer;f:Tfield); // weist ein Element dem dyn. arrays zu (mit dem index cluster[i,j])
// ...

Nach dem Kompilieren der Anwendung öffnet sich nun das Formular mit einem Button. Betätigt man diesen wird die procedure start; aufgerufen (wir befinden uns hier in der main unit (Unit1)).
start; soll nun alle Spielvorbereitungen erledigen (die "settings" einstellen).
Erster Schritt wäre hierbei die Länge von Cluster zu definieren um anschließend diesem die Elemente zuzuweisen. Allerdings gibt es hier schon die "Exception SIGSEGV" :cry:

Code: Alles auswählen

procedure TForm1.start;
begin
  // creating map
  showMessage('0'); // nach Betätigung des Buttons kommt diese Nachricht
  data.setCluster_dimensions(25,25); // hier wird die Exception ausgelöst
  showMessage('1'); // dieser output kommt nie zustande :(
end

Die procedure die ich versuche aufzurufen ist im letzten code-Ausschnitt dieses Beitrags. Die If-Abfrage trifft bis jetzt auf jeden Fall zu, und die Methode setLength(data.Cluster,x,y); wird aufgerufen, wobei die Parameter x,y jeweils den Integerwert von 25 betragen (da die Dimensionen dementsprechend vorerst immer 25*25 Felder sein sollen).

Code: Alles auswählen

 
procedure Tdata.setCluster_dimensions(x, y: integer);
begin
  // the map size (/ cluster) is limited by 128*128 Fields and has its minimum by 8*8
  if (x<8)or(y<8)or(x>128)or(y>128) then showMessage('ERROR : procedure Tdata.setCluster_dimensions(x, y: integer); '+sLinebreak+'parameters for dimensions are not in valid range ('+intToStr(x)+':'+intToStr(y)+')')
  else begin
    setLength(data.Cluster,x,y); // der Überltäter, wie ich vermute
  end;
end;


Wieso um alles in der Welt wird hier eine Exception ausgelöst ?
Ich meine, das array ist zwar private und ich kann nicht direkt darauf zugreifen, aber dafür habe ich ja die procedure setCluster_dimensions, die letztendlich nur das herkömmliche setLength aufruft.
Was soll denn an diesem Aufruft illeal sein ? .. setLength(Cluster,25,25);

mfG.
tightTannic
Zuletzt geändert von tightTannic am Sa 12. Aug 2017, 22:48, insgesamt 1-mal geändert.

tightTannic
Beiträge: 20
Registriert: Di 31. Jan 2017, 19:46
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit
Wohnort: Berlin

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von tightTannic »

Hier noch die Exception
Dateianhänge
expt2.JPG

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von wp_xyz »

An SetLength(cluster, 25, 25) ist nichts faul, aber an SetLength(data.Cluster, 25, 25). Denn letzteres setzt voraus, dass "data" existiert - ich nehme an, das ist eine Instanz der Klasse TData.

So wie ich die Code-Schnippsel interpretiere, bis du dem üblichen Anfänger-fehler aufgesessen, im Code einer Klasse Bezeichner für die Instanz zu verwenden. TData ist eine Klasse, und diese "kennt" die interne private Variable Cluster. Daher reicht es aus, in dem Code der das Verhalten der Klasse implementiert (d.h. da, wo in der Prcedure immer der Klassenname voransteht, also in TData.SetStructure_Dimensions) nur Cluster zu verwenden. Wenn du soäter die Instanz einer Klasse erzeugt hast (eine "Variable"), und von dieser aus auf den Cluster zugreifen musst, dann musst die natürlich data.Cluster schreiben, denn im aktuellen Kontext weiß dein Programm nicht welcher Cluster gemeint ist - du könntest ja auch mehrere Instanzen erzeugt haben. (Es wäre noch anzumerken, dass data.Cluster auch nicht funktionieren wird, da das Feld Cluster in der Klasse als private deklariert ist, also nur innerhalb der Klasse sichtbar ist).

Also: das folgende sollte funktionieren:

Code: Alles auswählen

Tdata = class
  private
    Cluster:array of array of TField;
  public
    procedure setCluster_dimensions(x,y:integer);
....
 
procedure Tdata.setCluster_dimensions(x, y: integer);
begin
  if (x<8)or(y<8)or(x>128)or(y>128) then showMessage('ERROR : procedure Tdata.setCluster_dimensions(x, y: integer); '+sLinebreak+'parameters for dimensions are not in valid range ('+intToStr(x)+':'+intToStr(y)+')')
  else begin
    setLength(Cluster,x,y);                   // <--- kein "Cluster" ohne vorangestelltes "data"
  end;
end;

tightTannic
Beiträge: 20
Registriert: Di 31. Jan 2017, 19:46
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit
Wohnort: Berlin

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von tightTannic »

(Wow schnelle Antowrt :oops: )

Es stimmt :) Ich habe eine Instanz von Tdata (data:Tdata) in der unit_data erstellt.
Und ja, ich bin in Klassen-erstellen regelrecht ein Deletant, da ich zuvor alles mit Records bewerkstelligt habe (auch in diesem Programm, wobei ich nun auf Klassen umsteige)
...
Danke, deinen Einwand habe ich Verstanden
Jedoch wird aufs neue die selbe Exception ausgespuckt :/

Code: Alles auswählen

procedure Tdata.setCluster_dimensions(x, y: integer);
begin
  // the map size (/ cluster) is limited by 128*128 Fields and has its minimum by 8*8
  if (x<8)or(y<8)or(x>128)or(y>128) then showMessage('ERROR : procedure Tdata.setCluster_dimensions(x, y: integer); '+sLinebreak+'parameters for dimensions are not in valid range ('+intToStr(x)+':'+intToStr(y)+')')
  else begin
    setLength(data.Cluster,x,y); // erste Version (die "falsche")
  end;
end

Code: Alles auswählen

procedure Tdata.setCluster_dimensions(x, y: integer);
begin
  // the map size (/ cluster) is limited by 128*128 Fields and has its minimum by 8*8
  if (x<8)or(y<8)or(x>128)or(y>128) then showMessage('ERROR : procedure Tdata.setCluster_dimensions(x, y: integer); '+sLinebreak+'parameters for dimensions are not in valid range ('+intToStr(x)+':'+intToStr(y)+')')
  else begin
    setLength(Cluster,x,y); // deine Version
  end;
end

Code: Alles auswählen

procedure Tdata.setCluster_dimensions(x, y: integer);
begin
  // the map size (/ cluster) is limited by 128*128 Fields and has its minimum by 8*8
  if (x<8)or(y<8)or(x>128)or(y>128) then showMessage('ERROR : procedure Tdata.setCluster_dimensions(x, y: integer); '+sLinebreak+'parameters for dimensions are not in valid range ('+intToStr(x)+':'+intToStr(y)+')')
  else begin
    setLength(self.Cluster,x,y); // geht auch nicht :(
  end;
end


Es wäre noch anzumerken, dass data.Cluster auch nicht funktionieren wird, da das Feld Cluster in der Klasse als private deklariert ist, also nur innerhalb der Klasse sichtbar ist

Um im Programmverlauf auf das Cluster zuzugreifen habe ich einige get-Funktionen, die mir das Cluster oder dessen einzelne Elemente ausgeben können, in Tdata definiert.

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von wp_xyz »

Ist in dem Augenblick, in dem SetCluster_Dimensions aufgerufen wird, die Instanz "data" schon erzeugt? Es reicht nicht, sie zu deklarieren (d.h. "data: TData"), sondern man muss auch den Constructor der Klasse aufrufen. Ich weiß ja nicht, was dein Programm macht und wie es aufgebaut ist, daher kann ich dir nicht sagen, wo die optimale Stelle dafür ist. Das folgende könnte höchstwahrscheinlich funktionieren

Code: Alles auswählen

procedure TForm1.start;
begin
  // creating map
  showMessage('0');
  if data = nil then
    data := TData.Create// Ich nehme mal an, der Constructor heißt "Create" und braucht keine Parameter, ansonsten einsetzen
  data.setCluster_dimensions(25,25);
  showMessage('1');
end;
 
// oder auch
 
procedure TForm1.Form1Create(Sender: TObject);   // Event-Handler für OnCreate von TForm1
begin
  data := TData.Create;
end;
 
// nicht vergessen: Alles was man erzeugt, muss man auch wieder wegräumen
procedure TForm1.Form1Destroy(Sender: TObject)// Event-Handler für OnDestroy von TForm1
begin
  data.Free;
end;

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von Mathias »

Jedoch wird aufs neue die selbe Exception ausgespuckt :/

Befindet sich so etwas oder was ähnliches in dem Code ?

Code: Alles auswählen

procedure TForm1.FormCreate(Sender: TObject);
begin
  Data := TData.Create// Data erzeugen
end;
 
procedure TForm1.FormDestroy(Sender: TObject);
begin
  Data.Free// Data freigeben
end;
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

tightTannic
Beiträge: 20
Registriert: Di 31. Jan 2017, 19:46
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit
Wohnort: Berlin

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von tightTannic »

Solche Con- und Destructor habe ich nicht :oops: .

Anscheinend für Klassen notwendig ;)

...

Das bedeuted also ich sollte für jede Klasse im Programm eine Create & Destroy Methode haben, und diese vor der Nutzung dieser Klassen aufrufen (!?)

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von Mathias »

Anscheinend für Klassen notwendig ;)

Ja dies ist zwingend notwendig, wen du eine Klasse nicht erzeugst, dann wird auch kein Speicher dafür reserviert, und wen man anschliessend darauf zugreift, dann knallt es.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

wp_xyz
Beiträge: 4869
Registriert: Fr 8. Apr 2011, 09:01

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von wp_xyz »

tightTannic hat geschrieben:Das bedeuted also ich sollte für jede Klasse im Programm eine Create & Destroy Methode haben, und diese vor der Nutzung dieser Klassen aufrufen (!?)

Im Prinzip ja. Allerdingst sieht man nicht bei jeder Klasse ein "Create" und eine "Destroy" im Quellcode. In diesem Fall wird der vom Vorfahren geerbte Con/Destructor verwendet. Da jede Klasse letztendlich von TObject abgeleitet ist, hat also jede Klasse einen Konstructor (Create) und einen Destructor (Destroy - den man übrigens nicht direkt aufruft, sondern über Free).

Du musst selbst einen eigenen Con/Destructor schreiben, wenn deine Klasse z.B Speicher anfordert, oder Hilfsklassen erzeugt - dies muss im Destruktor wieder aufgeräumt werden.

tightTannic
Beiträge: 20
Registriert: Di 31. Jan 2017, 19:46
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit
Wohnort: Berlin

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von tightTannic »

Danke euch,
ich rolle mal mit dem neuen Wissen über den Code. Möglicherweise funktioniert es dann ja *hust*

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von Mathias »

ich rolle mal mit dem neuen Wissen über den Code. Möglicherweise funktioniert es dann ja *hust*

In diesem Beispiel sieht man gut, wie man eine Klasse handhabt. TStringList ist auch eine Klasse.

Code: Alles auswählen

var
  sl: TStringList;
begin
  sl := TStringList.Create// Speicher reservieren
  sl.Add('Hello World !');   // Mache etwas mit der Klasse
  sl.Add('Hallo Welt !');
  ShowMessage(sl.Text);
  sl.Free;                   // Speicher wieder frei geben.
end;
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

tightTannic
Beiträge: 20
Registriert: Di 31. Jan 2017, 19:46
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit
Wohnort: Berlin

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von tightTannic »

Das freisetzen des Speichers (/ Zerstören der Klassen) hab ich noch implementiert, allerdings behalte ich das im Hinterkopf, für den Zeitpunkt wenn es so weit ist !

Ich habe die procedure Tdata.setCluster_dimensions erweitert :
Da die Elemente des arrays Cluster vom Typ Tfield=class sind, müssen diese wohl auch zuerst construiert werden (afaik weil sie Klassen sind und Speicher benötigen)

Code: Alles auswählen

procedure Tdata.setCluster_dimensions(x, y: integer);
  var k,l:integer;
begin
  // the map size (/ cluster) is limited by 128*128 Fields and has its minimum by 8*8
  if (x<8)or(y<8)or(x>128)or(y>128) then showMessage('ERROR : procedure Tdata.setCluster_dimensions(x, y: integer); '+sLinebreak+'parameters for dimensions are not in valid range ('+intToStr(x)+':'+intToStr(y)+')')
  else begin
    setLength(self.Cluster,x,y);
  end;
// neues    ___________________________________________________________________________________________________________________________________________
  for k:=0 to x-1 do
    for l:=0 to y-1 do begin 
      self.Cluster[x,y]:=Tfield.create; // Exception
      showMessage('Es hat tatsächlich bis hierher funktioniert !'); // leider bekomme ich diese Meldung zur Laufzeit nie zu lesen :(
end;

Die Felder des Clusters (also die Teilabschnitte der map) besitzen "Quellen" für Holz, Stein, Nahrung etc. die im Spielverlauf "abgebaut" werden, um sie für, naja solche Sachen zu gebrauchen, für die man diese eben in Spielen brauchen könnte...
Jedenfalls sind diese Quellen (Tsource=class) auch Klassen, weshalb diese ebenfalls mit den Feldern konstruiert werden (im constructor von Tfield) :

Code: Alles auswählen

constructor TField.Create;
begin
  inherited Create;
  self.source:=Tsource.Create; // Tsource.Create ist zwar nicht von mir im Quellcode eigenhändig definiert worden, jedoch sollte diese Methode ja vererbt werden (hier wird der Fehler also nicht sein ?)
end


Wird nun setCluster_dimensions ausgeführt, bleibt es mit einer Exception SIGSEGV hängen, wenn die Elemente des arrays konstruiert werden.

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von Mathias »

Ich habe bei dir gerade etwas gesehen

Code: Alles auswählen

self.Cluster[x,y]:=Tfield.create; // Exception 

das kann nicht gehen, dies müsste

Code: Alles auswählen

self.Cluster[k, l]:=Tfield.create; // Exception 
heissen.

PS: Das "self* kannst du sparen.
PSS: Was du noch aufpassen muss, wen du zur Laufzeit mehrmals die Grösse der Array änderst. Zuerst aufraumen und dann wieder initialisieren.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

tightTannic
Beiträge: 20
Registriert: Di 31. Jan 2017, 19:46
OS, Lazarus, FPC: Winux (L 0.9.xy FPC 2.2.z)
CPU-Target: xxBit
Wohnort: Berlin

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von tightTannic »

ah ja, natürlich die Zählvariablen verwechseln -.-

danke, das PSS ist für mich Gold wert, oder zumindest mehrere Minuten Fehlersuche :)

Mathias
Beiträge: 6160
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Re: Dynamisches Array - Exception SIGSEGV bei setLength

Beitrag von Mathias »

Die Anwendung soll ein kleines 2D-Spiel werden, wobei es eine 2-dimensionale Map gibt (vergleichbar mit Schiffeversenken, Schach etc.). Diese soll mit einem dynamischen Array (Cluster) realisiert werden, welches sich in einer separaten Unit (unit_data) in der Klasse data:Tdata befindet (dynamisches array, weil der Spieler die Dimensionen der Karte verändern kann).

Noch eine Idee, evtl. wäre ein Nachkommen von einem TPanel für dein Spiel geeignet, ich denke mal du will noch Bilder auf die Kacheln zeichnen.
Mit den TPanel kannst du natürlich auch eine 2D-Array machen. :wink:
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Antworten