Zweidimensionales dynamisches Array an andere Unit übergeben.

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
BerlinerBaer
Beiträge: 16
Registriert: Sa 9. Jan 2021, 15:07
OS, Lazarus, FPC: Windows (L 2.0.10 FPC 3.2.0)
CPU-Target: 32/64Bit

Zweidimensionales dynamisches Array an andere Unit übergeben.

Beitrag von BerlinerBaer »

Hallo Freunde des Lazarus.

Zuerst einmal frage ich, ob es der oder das Array heißt?

Nun zum eigentlichen Punkt. Ich habe zwei Units und ein zweidimensionales dynamisches Array.

Das Erstellen des Arrays funktioniert soweit ganz gut. Erklären brauche ich das, denke ich mal nicht. Wenn Fragen sind, immer her damit.

Code: Alles auswählen

// Unit 1
procedure TFormTestErstellen.Button1Click(Sender: TObject);
var
  TestArray : Array of Array[0..1] of String;
  i : Integer;
begin
  SetLength(TestArray, LVTest.Items.Count);
  for i:=0 to High(TestArray) do
  begin
    // Daten aus der ListView LVTest in TestArray speichern
    TestArray[i][0]:=LVTest.Items[i].Caption;
    TestArray[i][1]:=LVTest.Items[i].SubItems[1];
    // ShowMessage(TestArray[i][0]+' '+TestArray[i][1]);
    // Für Testzwecke habe ich ShowMessage verwendet.
  end;
  // Übergabe des Arrays an Unit2 also uProzeduren
  uProzeduren.Test(@TestArray);
  // Array auf Null setzen.
  SetLength(TestArray, 0);
end;
Wie bereits erwähnt, funktioniert das mit dem Erstellen des Arrays wunderbar. Nun übergebe ich das Array an die zweite Unit. Dazu habe ich unmittelbar unter "uses" den "type" definiert:

Code: Alles auswählen

// Unit 2
uses
  Classes, SysUtils; // etc. pp.

type
  TestArray = Array of Array of String;
  
  procedure Test(TestAusgabe : TestArray);  

implementation

uses
  Unit1;   
 
procedure Test(TestAusgabe : TestArray);
var
  i : Integer;
begin
  ShowMessage(IntToStr(Low(Produkte))+' | '+IntToStr(High(Produkte))); // Zum Testen, ob Low und High korrekt sind.
  // Low ist bei 0 und High liegt bei 2951136, obwohl High bei 0 liegen müsste.
  for i:=Low(TestAusgabe) to High(TestAusgabe) do
  begin
    ShowMessage(TestAusgabe[i][0]+' - '+TestAusgabe[i][1]);
  end;
end;
Das Array wurde damit übergeben. Nun komme ich zum Problem:
TextAusgabe[1][0] - TextAusgabe[1][1] gibt er noch richtig aus. Was danach passiert, ist für mich nicht verständlich.
Er ruft danach weitere 4 mal ShowMessage auf mit einem Bindestrich. Danach kommt die Fehlermeldung: SIGSEGV bei Adresse 10000B0F5

Nun habe ich einfach mal getestet, ob Low(TestAusgabe) und Heigh(TestAusgabe) korrekt sind. Dabei fiel mir auf, dass Low(TestAusgabe) bei 0 liegt, was richtig ist, was mir jedoch spanisch vorkommt, ist die Tatsache, dass High(TestAusgabe) bei 2951136 liegt, obwohl High eigentlich davon abhängig ist, wie viele "Spalten" gegeben sind*. Woher die Zahl kommt, weiß ich nicht.

Selbst, wenn ich in der zweiten Unit das Array unter "´type" anpasse, also begrenze (ProduktArray = Array of Array[0..1] of String;), kommt die selbe Zahl (2951136) bei raus.

* Definition meines Arrays:
Array[0, 1] = Zeile 0, Spalte 1
Array[1, 1] = Zeile 1, Spalte 1

Anmerken möchte ich, dass ich mich heute zum ersten mal mit Arrays auseinandergesetzt habe. Also erwartet bitte von mir nicht allzu viel Fachwissen.

Hat jemand eine Idee?
Die deutsche Rechtschreibung ist Freeware. Man darf sie kostenlos nutzen. Allerdings ist sie nicht Open Source. Das heißt, sie darf weder verändert, noch in veränderter Form veröffentlicht werden.

Benutzeravatar
fliegermichl
Lazarusforum e. V.
Beiträge: 790
Registriert: Do 9. Jun 2011, 09:42
OS, Lazarus, FPC: Winux (L 2.0.11 FPC 3.2)
CPU-Target: 32/64Bit
Wohnort: Echzell

Re: Zweidimensionales dynamisches Array an andere Unit übergeben.

Beitrag von fliegermichl »

Intern ist ein array of array bereits ein Zeiger. Wenn du bei der Übergabe @testarray angibst, übergibst du sozusagen einen Zeiger auf einen Zeiger.
Besser du deklarierst das array als typ und übergibst dann eine einfache Variable diesen Typs.

Code: Alles auswählen

// die Typdeklaration sollte in unit2 stattfinden, da diese von Unit1 eingebunden wird und der Typ somit auch da zur Verfügung steht.
type
 TMyTestArray = array of array[0..1] of string;
 
// Unit 1
procedure TFormTestErstellen.Button1Click(Sender: TObject);
var
  TestArray : TMyTestArray;
  i : Integer;
begin
  SetLength(TestArray, LVTest.Items.Count);
  for i:=0 to High(TestArray) do
  begin
    // Daten aus der ListView LVTest in TestArray speichern
    TestArray[i][0]:=LVTest.Items[i].Caption;
    TestArray[i][1]:=LVTest.Items[i].SubItems[1];
    // ShowMessage(TestArray[i][0]+' '+TestArray[i][1]);
    // Für Testzwecke habe ich ShowMessage verwendet.
  end;
  // Übergabe des Arrays an Unit2 also uProzeduren
  uProzeduren.Test(TestArray);
  // Array auf Null setzen.
  SetLength(TestArray, 0);
end;

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

Re: Zweidimensionales dynamisches Array an andere Unit übergeben.

Beitrag von wp_xyz »

Vielleicht übersehe ich das. Aber ich finde im Code des 1.Posts nirgendwo ein SetLength für den 2.Array-Index; ich sehe nur

Code: Alles auswählen

SetLength(TestArray, LVTest.Items.Count);
Richtig wäre

Code: Alles auswählen

SetLength(TestArray, LVTest.Items.Count, 2);
[EDIT]
Ach doch: in der Deklaration steht

Code: Alles auswählen

Array of Array[0..1] of String

BerlinerBaer
Beiträge: 16
Registriert: Sa 9. Jan 2021, 15:07
OS, Lazarus, FPC: Windows (L 2.0.10 FPC 3.2.0)
CPU-Target: 32/64Bit

Re: Zweidimensionales dynamisches Array an andere Unit übergeben.

Beitrag von BerlinerBaer »

Ich hatte erst mit TStringList gearbeitet, doch schnell gemerkt, dass es sich zwar auch um Arrays handelt, jedoch nicht mehrdimensional anwendbar ist.
fliegermichl hat geschrieben:
Mi 20. Jan 2021, 16:03
Intern ist ein array of array bereits ein Zeiger. Wenn du bei der Übergabe @testarray angibst, übergibst du sozusagen einen Zeiger auf einen Zeiger.
Besser du deklarierst das array als typ und übergibst dann eine einfache Variable diesen Typs.
Wenn ich das jetzt richtig verstanden habe, liegt das Problem wirklich nur an dem "Zeiger" und an der Bezeichnung von TestArray?
Was das mit dem Zeiger so aufsich hat, verstehe ich noch nicht ganz.
fliegermichl hat geschrieben:
Mi 20. Jan 2021, 16:03

Code: Alles auswählen

// die Typdeklaration sollte in unit2 stattfinden, da diese von Unit1 eingebunden wird und der Typ somit auch da zur Verfügung steht.
type
 TMyTestArray = array of array[0..1] of string;
Diese fand und findet ja in Unit2 statt. Alles andere würde keinen Sinn machen, da ich ggf. die Deklaration doppelt ausführen müsste. Weiterhin benutze ich die Unit2, also uProzeduren auch für andere Units.

Ich danke dir jedenfalls recht herzlich für die Korrektur meines fehhlerhaften Skriptes.
wp_xyz hat geschrieben:
Mi 20. Jan 2021, 16:13
Vielleicht übersehe ich das. Aber ich finde im Code des 1.Posts nirgendwo ein SetLength für den 2.Array-Index; ich sehe nur

Code: Alles auswählen

SetLength(TestArray, LVTest.Items.Count);
Richtig wäre

Code: Alles auswählen

SetLength(TestArray, LVTest.Items.Count, 2);
[EDIT]
Ach doch: in der Deklaration steht

Code: Alles auswählen

Array of Array[0..1] of String
Nunja, was richtig ist, entscheidet im Grunde genommen ihr, da ich in puncto Arrays wirklich Neuland betrete.
Ich verwendete die andere Variante, da mir Lazarus das zum Einen so vorgeschlagen und zum Anderen mir was anderes sagt:

Code: Alles auswählen

SetLenght(var s: String; NewLength: Integer)
Hier ist die Angabe einscheinend nicht flexibel genug, um mir zu sagen, dass ich auch hier einen zweiten Wert angeben kann. Demzufolge entschied ich mich für meine Variante.
Tatsache ist, ich habe mich jetzt für dich* entschieden, da es offensichtlich übersichtlicher ist. Wozu also muss das Rad neu erfinden?

Jedenfalls möchte ich euch beiden für die Hilfe danken. Es klappt genau so, wie ich es mir vorgestellt habe. :)

* Gemeint ist natürlich dein Vorschlag.
Die deutsche Rechtschreibung ist Freeware. Man darf sie kostenlos nutzen. Allerdings ist sie nicht Open Source. Das heißt, sie darf weder verändert, noch in veränderter Form veröffentlicht werden.

PascalDragon
Beiträge: 227
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Zweidimensionales dynamisches Array an andere Unit übergeben.

Beitrag von PascalDragon »

BerlinerBaer hat geschrieben:
Mi 20. Jan 2021, 15:15

Code: Alles auswählen

var
  TestArray : Array of Array[0..1] of String;
begin
  // ...
  uProzeduren.Test(@TestArray);
end;

Code: Alles auswählen

type
  TestArray = Array of Array of String;
  
  procedure Test(TestAusgabe : TestArray);
Nein, einfach nur Nein.

1. Mixe nicht dynamische mit statischen Arrays. Die sind intern unterschiedlich aufgebaut. Entweder du deklarierst den Typ TestArray auch als array of array[0..1] of String oder du deklarierst deine lokale Variable als array of array of String und machst ein SetLength(TestArray, LVTest.Items.Count, 2);.

2. Warum übergibst du einen Zeiger auf TestArray an Test, das doch überhaupt gar keinen Zeiger erwartet? (Und warum lässt der Compiler das zu? :shock: )
BerlinerBaer hat geschrieben:
Mi 20. Jan 2021, 19:46
Nunja, was richtig ist, entscheidet im Grunde genommen ihr, da ich in puncto Arrays wirklich Neuland betrete.
Ich verwendete die andere Variante, da mir Lazarus das zum Einen so vorgeschlagen und zum Anderen mir was anderes sagt:

Code: Alles auswählen

SetLenght(var s: String; NewLength: Integer)
Hier ist die Angabe einscheinend nicht flexibel genug, um mir zu sagen, dass ich auch hier einen zweiten Wert angeben kann. Demzufolge entschied ich mich für meine Variante.
Es empfiehlt sich auch immer einen Blick in die Dokumentation zu werfen. Konkret hier und hier.
FPC Compiler Entwickler

BerlinerBaer
Beiträge: 16
Registriert: Sa 9. Jan 2021, 15:07
OS, Lazarus, FPC: Windows (L 2.0.10 FPC 3.2.0)
CPU-Target: 32/64Bit

Re: Zweidimensionales dynamisches Array an andere Unit übergeben.

Beitrag von BerlinerBaer »

PascalDragon hat geschrieben:
Do 21. Jan 2021, 09:01
1. Mixe nicht dynamische mit statischen Arrays. Die sind intern unterschiedlich aufgebaut.
Ich habe eine dynamische Anzahl an "IDs" und eine feste Anzahl an zur ID zugehörigen Werten. Oder sollte ich dann lieber zwei mal SetLenght und zwei For-To-Do-Schleifen arbeiten?

Code: Alles auswählen

var
  i, j : integer;
begin

  SetLength(TestArray, LVTest.Items.Count, 2);
   for i:=0 to High(TestArray) do
   begin
     for j:=0 to { ??? } do // ?????
     begin
       TestArray[i][j]:=LVTest.Items[i].Caption;
       TestArray[i][j]:=LVTest.Items[i].SubItems[j];
     end;
   end;
end;
Das würde für mich keinen Sinn machen. Ich wüsste noch nicht mal, wie ich die j-Schleife weiter gestalten müsste. Alleine beim Aufbringen der Überlegungen, wie ich das am besten umsetzen könnte, würde ich mir eher Gedanken nach alternativen Möglichkeiten suchen. Zumal mein erster Gedanke war mit TStringList zu arbeiten, wo ich mir jedoch nicht noch mehr Arbeit machen wollte, um die einzelnen Strings auseinander zu pflücken.

PascalDragon hat geschrieben:
Do 21. Jan 2021, 09:01
Entweder du deklarierst den Typ TestArray auch als array of array[0..1] of String oder du deklarierst deine lokale Variable als array of array of String und machst ein SetLength(TestArray, LVTest.Items.Count, 2);.
Hier beschreibst du doch, dass dynamische und statische Arrays vermischt werden, oder verstehe ich das jetzt falsch?

Code: Alles auswählen

var arr1 : array of strings; // Ich denke, dass das dynamisch ist.
    arr2 : array[0..2] of strings; // Das wäre m. M. n. statisch.
Da ich aber einmal eine unbekannte Anzahl an Werten (siehe oben: IDs) habe und einmal feste Werte, darf ich das doch vermischen.
Oder sind zwei vorneinander getrennte Arrays effektiver?

PascalDragon hat geschrieben:
Do 21. Jan 2021, 09:01
2. Warum übergibst du einen Zeiger auf TestArray an Test, das doch überhaupt gar keinen Zeiger erwartet? (Und warum lässt der Compiler das zu? :shock: )
Was ein Zeiger ist, weiß ich noch immer nicht. Ich denke, wenn mein kleines Projekt fertig ist, werde ich mir wohl noch so einiges an Fachwissen aneignen müssen.

PascalDragon hat geschrieben:
Do 21. Jan 2021, 09:01
Es empfiehlt sich auch immer einen Blick in die Dokumentation zu werfen. Konkret hier und hier.
Danke, die Seiten kannte ich vorher noch nicht.
Die deutsche Rechtschreibung ist Freeware. Man darf sie kostenlos nutzen. Allerdings ist sie nicht Open Source. Das heißt, sie darf weder verändert, noch in veränderter Form veröffentlicht werden.

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

Re: Zweidimensionales dynamisches Array an andere Unit übergeben.

Beitrag von wp_xyz »

Du kannst machen was du willst, musst aber bei deiner Entscheidung bleiben. Wenn du das 2D-Array als "array of array[0..1] of..." deklarierst, dann darfst du es nicht an eine Funktion übergeben, in deren Parameter es als "array of array of..." (ohne fest Grenzen für das innere Array) deklariert ist, denn der Speicher ist ganz unterschiedlich belegt.

Bei einem statischen Array ("array [0..1] of...") ist eine Variable gemeint, die zwei aufeinanderfolgende Werte des Typs umfasst, der hinter "of" genannt ist.

Bei einem dynamischen Array ("array of ...") ist die Größe zur Kompilierungszeit nicht bekannt, und was hier als "array" bezeichnet ist, ist eigentlich ein Zeiger, der dann, wenn später die Array-Größe per SetLength() definiert wurde, auf den Speicherblock verweist, in dem die Array-Daten liegen.

Ein 2D-Array "array of array" ist damit zunächst ein Array von Zeigern (das erste "array"). Jeder Zeiger zeigt wieder auf ein Array von Zeigern (das zweite "array"), von denen jeder auf die irgendwo im Speicher hintereinander liegenden Nutzdaten zeigt (Bei einem Array of Array of String kommt noch eine dritte Stufe dazu, denn ein "String" ist in modernem Pascal auch wieder nur ein Zeiger, der auf das eigentliche Zeichen-Array zeigt).

Das 2D-Array "array of array[0..1]" ist auch wieder ein Array von Zeigern (das erste "array"), aber diese zeigen nicht wieder auf ein Zeiger-Array, sondern auf einen Block mit zwei hintereinander liegenden Nutz-Daten.

Ich weiß, es ist ein Gehirn-Twister... Zur Verdeutlichung habe ich versucht, das bildlich darzustellen: die orangen Kästchen sind Pointer, die Pfeile symbolisieren, worauf sie zeigen, und die blauen Kästchen sind "Nutzvariablen", hier ein Typ TZiffer, der die Ziffern 0...9 umfasst. Aneinander angrenzende Kästchen stellen jeweil ein "Array" dar.
Dateianhänge
arrays.png
arrays.png (13.4 KiB) 250 mal betrachtet

BerlinerBaer
Beiträge: 16
Registriert: Sa 9. Jan 2021, 15:07
OS, Lazarus, FPC: Windows (L 2.0.10 FPC 3.2.0)
CPU-Target: 32/64Bit

Re: Zweidimensionales dynamisches Array an andere Unit übergeben.

Beitrag von BerlinerBaer »

wp_xyz hat geschrieben:
Do 21. Jan 2021, 16:02
Wenn du das 2D-Array als "array of array[0..1] of..." deklarierst, dann darfst du es nicht an eine Funktion übergeben, in deren Parameter es als "array of array of..." (ohne fest Grenzen für das innere Array) deklariert ist, denn der Speicher ist ganz unterschiedlich belegt.
Das ergibt natürlich einen Sinn.

Und deine Grafik, die ist mehr als selbsterklärend.

Vielen Dank für den Aufwand, den ihr für mich betreibt. :)
Die deutsche Rechtschreibung ist Freeware. Man darf sie kostenlos nutzen. Allerdings ist sie nicht Open Source. Das heißt, sie darf weder verändert, noch in veränderter Form veröffentlicht werden.

PascalDragon
Beiträge: 227
Registriert: Mi 3. Jun 2020, 07:18
OS, Lazarus, FPC: L 2.0.8, FPC Trunk, OS Win/Linux
CPU-Target: Aarch64 bis Z80 ;)
Wohnort: München

Re: Zweidimensionales dynamisches Array an andere Unit übergeben.

Beitrag von PascalDragon »

BerlinerBaer hat geschrieben:
Do 21. Jan 2021, 15:14
PascalDragon hat geschrieben:
Do 21. Jan 2021, 09:01
1. Mixe nicht dynamische mit statischen Arrays. Die sind intern unterschiedlich aufgebaut.
Ich habe eine dynamische Anzahl an "IDs" und eine feste Anzahl an zur ID zugehörigen Werten. Oder sollte ich dann lieber zwei mal SetLenght und zwei For-To-Do-Schleifen arbeiten?
Nein, du brauchst deine Schleife nicht anzupassen. Die Verwendung ist diesem Fall die gleiche wie bei dem statischen Array außer, dass du auch die zweite Dimension korrekt belegen musst.
BerlinerBaer hat geschrieben:
Do 21. Jan 2021, 15:14
PascalDragon hat geschrieben:
Do 21. Jan 2021, 09:01
Entweder du deklarierst den Typ TestArray auch als array of array[0..1] of String oder du deklarierst deine lokale Variable als array of array of String und machst ein SetLength(TestArray, LVTest.Items.Count, 2);.
Hier beschreibst du doch, dass dynamische und statische Arrays vermischt werden, oder verstehe ich das jetzt falsch?
Da habe ich mich nicht genau genug ausgedrückt, ich bitte um Entschuldigung. Was ich meinte, weiße nicht ein statisches Array an ein dynamisches Array zu oder anders herum. Das gilt auch wenn du ein dynamisches Array aus statischen Arrays an ein dynamisches Array aus dynamischen Arrays übergibt.

Es ist natürlich erlaubt Typen/Variablen zu deklarieren die aus verschiedenen Mischungen von dynamischen und statischen Arrays bestehen, diese sind jedoch nicht kompatibel untereinander.
BerlinerBaer hat geschrieben:
Do 21. Jan 2021, 15:14
PascalDragon hat geschrieben:
Do 21. Jan 2021, 09:01
2. Warum übergibst du einen Zeiger auf TestArray an Test, das doch überhaupt gar keinen Zeiger erwartet? (Und warum lässt der Compiler das zu? :shock: )
Was ein Zeiger ist, weiß ich noch immer nicht. Ich denke, wenn mein kleines Projekt fertig ist, werde ich mir wohl noch so einiges an Fachwissen aneignen müssen.
Ein Zeiger (im Englischen Pointer) ist erstmal nur ein Verweis auf eine Speicherstelle. Was dann dort liegt kann relativ frei interpretiert werden. Nimm mal folgendes:

Code: Alles auswählen

var
  pb: PByte; // PByte ist in Unit System deklariert als ^Byte
  b: Byte;
  l: LongInt;
begin
  b := $ab;
  l := $12345678;
  pb := @b;
  Writeln(HexStr(pb^, 2)); // gibt "ab" aus
  // du kannst aber auch z.B. nen Zeiger auf einen LongInt als Zeiger auf ein Byte interpretieren
  bp := PByte(@l);
  Writeln(HexStr(pb^, 2)); // little endian: "78", big endian: "12"
end.
Wie wp_xyz erklärt hat, ist ein dynamisches Array intern eben eine solche Zeigervariable ein statisches Array ist aber ein Speicherbereich (auf den dann wiederum gezeigt werden kann). Ein array of array[0..1] of String ist also anders aufgebaut als ein array of array of String und du darfst also nicht einfach das eine an das andere übergeben.

Aber ja, es empfiehlt sich zumindest über Zeiger Bescheid zu wissen, auch wenn man sie in (Object) Pascal im Großen und Ganzen recht gut vermeiden kann (wobei sie "unter der Haube" viel Verwendung finden).
FPC Compiler Entwickler

Benutzeravatar
Maik81SE
Beiträge: 191
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Raspian PI Desktop; Pi4 (Lazarusfpcupdeluxe/FPC 3.2.0)
CPU-Target: i386; arm; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Zweidimensionales dynamisches Array an andere Unit übergeben.

Beitrag von Maik81SE »

PascalDragon hat geschrieben:
Fr 22. Jan 2021, 09:29
Aber ja, es empfiehlt sich zumindest über Zeiger Bescheid zu wissen, auch wenn man sie in (Object) Pascal im Großen und Ganzen recht gut vermeiden kann (wobei sie "unter der Haube" viel Verwendung finden).
Genau dieser Satz trifft den Nagel auf den Kopf...
Ich weiß nicht, wie es im Heutigen Lehrplan ausschaut, aber in den 90ern (Berufsschule) was dies definitiv NICHT enthalten.

Zumindest sollten da übergreifend grundlagen gelegt werden. egal ob pascal, c, oder welche sprache auch immer...

Zumindest kann ich sicherlich für mein Problem etwas an Grundlagen mitnehmen..

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 10.6 with Lazarus 2.1.0 r64080 & FPC 3.0.4 x86_64-linux-gkt2&Code:Blocks
Ubuntu 18.10 Studio
Pi4 -> Lazarus-IDE v2.0.0+dfsg-2 rDebian Package .0.0+dfsg-2[ & FPC 3.0.4

Antworten