Dynamische Arrays

Für Fragen zur Programmiersprache auf welcher Lazarus aufbaut
Mathias
Beiträge: 6914
Registriert: Do 2. Jan 2014, 17:21
OS, Lazarus, FPC: Linux (die neusten Trunk)
CPU-Target: 64Bit
Wohnort: Schweiz

Dynamische Arrays

Beitrag von Mathias »

Ist die Lösung mit SetLength so ideal, oder kann dies zu Fehler führen.

Code: Alles auswählen

 
type
    TVector = array[0..2] of GLfloat;
    TDynamischHalbZylinder = array of TVector;
 
 
function BerechneDynamischHalbZylinder: TDynamischHalbZylinder;
var
  Zaehler: integer;
 
  function v(x, y, z: GLfloat): TVector;
  begin
    v[0] := x;
    v[1] := y;
    v[2] := z;
 
    Inc(Zaehler);
    SetLength(BerechneDynamischHalbZylinder, Length(BerechneDynamischHalbZylinder) + 1);
  end;
 
  procedure Seite;
  var
    i: integer;
  begin
    for i := 0 to 36 do begin
      with Kreistab[i] do begin
        x := Sin1(i * 10) * 3;
        y := Cos1(i * 10) * 3;
      end;
    end;
 
    Zaehler := -1;
    for i := 0 to 17 do begin
      with KreisTab[i + 0] do Result[Zaehler] := v(x, 4, y); // Umfang
      with KreisTab[i + 1] do Result[Zaehler] := v(x, 4, y);
      with KreisTab[i + 1] do Result[Zaehler] := v(x, -4, y);
 
      with KreisTab[i + 0] do Result[Zaehler] := v(x, 4, y);
      with KreisTab[i + 1] do Result[Zaehler] := v(x, -4, y);
      with KreisTab[i + 0] do Result[Zaehler] := v(x, -4, y);
 
      with KreisTab[i + 0] do Result[Zaehler] := v(x, 4, y); // Stirnseite;
      with KreisTab[i + 1] do Result[Zaehler] := v(0, 4, 0);
      with KreisTab[i + 1] do Result[Zaehler] := v(x, 4, y);
 
      with KreisTab[i + 1] do Result[Zaehler] := v(x, -4, y); // Stirnseite;
      with KreisTab[i + 1] do Result[Zaehler] := v(0, -4, 0);
      with KreisTab[i + 0] do Result[Zaehler] := v(x, -4, y);
    end;
  end;
 
begin
  SetLength(Result, 1);
  Seite;
  SetLength(BerechneDynamischHalbZylinder, Length(BerechneDynamischHalbZylinder) - 1);
end;  
Zuletzt geändert von Mathias am So 12. Jan 2014, 20:23, insgesamt 1-mal geändert.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Dynamische Arrays

Beitrag von Michl »

Wöllte ich sowas machen, würde ich mit Variablen arbeiten, mit denen ich zuerst die benötigte Speichergröße berechne. Dann würde ich mir den Speicher holen (SetLength). Dann würden diese Variablen die Bereiche der Zählvariablen definieren.

Bei jedem SetLength von Length+1 muss das Betriebssystem einen neuen zusammenhängenden Speicherbereich bereitstellen. Dies mag bei einem kleinen Array kaum ins Gewicht fallen, bei größeren Arrays kann das zu einem richtigen Zeitfresser werden.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

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

Re: Dynamische Arrays

Beitrag von Mathias »

Sehe ich das richtig ?
Jedesmal wen die Array vergrössert wird und es zusammen keinen Platz im an der aktuellen Stelle hat, dann wird die Array an einen anderen Speicherort kopiert.
Ich habe im obigen Programm eine write-Zeile eingefügt.

Code: Alles auswählen

  function v(x, y, z: GLfloat): TVector;
  begin
    v[0] := x;
    v[1] := y;
    v[2] := z;
    Inc(Zaehler);
    SetLength(BerechneCylinder, Length(BerechneCylinder) + 1);
    write(Format('%10d', [integer(Pointer(BerechneCylinder))]));
  end;   
Da kommt folgendes Ergebnis:

Code: Alles auswählen

   2740408   2837488   2837488   2837488   2837488   3197488   3197488   3197488
   3197488   3197488   3197488   3197488   3197488 237255536 237255536 237255536
 237255536 237255536 237255536 237255536 237255536 237255536 237255536 237255536
 237255536 237255536 237255536 237255536 237255536   3162624   3162624   3162624
   3162624   3162624   3162624   3162624   3162624   3162624   3162624   3162624
   3162624   3162624   3162624   3162624   3162624   3162624   3162624   3162624
   3162624   3162624   3162624   3162624   3162624   3162624   3162624   3162624
   3162624   3162624   3162624   3162624   3162624   3162624   3162624   3162624
   3162624   3162624   3162624   3162624   3162624   3162624   3162624   3162624
   3162624   3162624   3162624   3162624   3162624   3162624   3152128   3152128
   3152128   3152128   3152128   3152128   3152128   3152128   3152128   3152128
   3152128   3152128   3152128   3152128   3152128   3152128   3152128   3152128
   3152128   3152128   3152128   3152128   3152128   3152128   3152128   3152128
 
 
Im Hauptprogramm steht folgendes;

Code: Alles auswählen

var
    vec: array of TVector;
begin 
  SetLength(vec, xxx);  // Isi diese Zeile überflüssig, es funktioniert ohne problemlos.
  vec := BerechneHalbZylinder;    
  ....
 
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Dynamische Arrays

Beitrag von Socke »

Mathias hat geschrieben:Sehe ich das richtig ?
Jedesmal wen die Array vergrössert wird und es zusammen keinen Platz im an der aktuellen Stelle hat, dann wird die Array an einen anderen Speicherort kopiert
Dazu sagt die Doku etwas: http://www.freepascal.org/docs-html/pro ... 280008.4.1
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Benutzeravatar
Maik81SE
Beiträge: 327
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.4 FPC 3.2.2)
CPU-Target: x86-64; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Dynamische Arrays

Beitrag von Maik81SE »

Moin,

in Diesem zusammenhang habe ich auch mal eine Frage.

ich bin Leider auf ein Array angewiesen, welchen in Erster Linie Dynamisch ist, jedoch in der 2. Dimension auf ein Statisches Array of String zugreift.

Im Grunde läuft das soweit auch ganz ok, wenn ich jedoch nun auf einer TiniFile die Daten lese und das Array damit beschreiben will, erhalte ich eine einen Zugriffsfehler.

Hier mal der Code, wie das aufgeschlüßelt ist.

Code: Alles auswählen

procedure TPlan.GetKabel(index: Int64; Value: array of TKabel);
var Temp : TStringlist;
    s: String;
    a: int64;
begin
  fData.Filename := Format('%s', [fData.Geraet + '.vpl']);
  if FileExists(fData.Filename) then begin
    with TIniFile.Create(fData.Filename) do try
      for A:= 0 to index do begin
          s:= Format('[%s]', ['Leitung'+IntToStr(a)]);
          Value[a, 0] := ReadString(s, 'Quelle'              , '');
          Value[a, 1] := ReadString(s, 'Kontaktart Quelle'   , '');
          Value[a, 2] := ReadString(s, 'Leitungstyp'         , '');
          Value[a, 3] := ReadString(s, 'Leitungsquerschnitt' , '');
          Value[a, 4] := ReadString(s, 'Leitungslänge'       , '');
          Value[a, 5] := ReadString(s, 'Leitungsfarbe'       , '');
          Value[a, 6] := ReadString(s, 'Kontaktart Ziel'     , '');
          Value[a, 7] := ReadString(s, 'Zielkontakt'         , '');
      end;
    finally
      Free;
    end;
  end;
end;
Definiert ist der Type TKabel wie folgt.

Code: Alles auswählen

 TKabel = Array[0..7] of String;
Der aufruf erfolgt via

Code: Alles auswählen

procedure TForm2.MenuItem9Click(Sender: TObject);
var a: Int64;
    b: byte;
begin
  if OpenDialog1.Execute then begin
    Plan:= TPlan.Create;
    for B:= 1 to Length(OpenDialog1.FileName) - 4 do Plan.fData.Geraet:= Plan.fData.Geraet + OpenDialog1.FileName[b];
    PlanEdit.StringGrid1.RowCount:= Plan.fData.index + 5;
    Plan.GetKabel(Plan.fData.Index, Plan.fKabel);
    Caption:= IntToStr(Plan.fData.index);
    for a:= 0 to Plan.fData.index do begin
      for b:= 0 to 7 do begin
        if Plan.fKabel[a, b] = '' then begin
        Caption:='Datensatz: ' + intToStr(a) + '|' + IntToSTr(b) + ' Ist LEER.';
        PlanEdit.Caption:= 'Bitte Prüfen Sie die Datei: ' + Plan.fData.Filename;
        PlanEdit.StringGrid1.Cells[b+1, a+1]:= 'Keine Daten :(';
 //       PlanEdit.Caption := Plan.fKabel[1, 1];
        end;
      end;
    end;
  end;
  PlanEdit.ShowModal;
end;
Bin für alle Vorschläge offen.

Nachtrag. Der Grund, warum ich auf diese Art von Arry zugreifen muß, liegt darin, das ich einen Verdrahtungsplanner schreibe, der auch gut mal mehrere Tausend Leitungen enthalten kann. Abhängig davon, was gerade aufgebaut wird...
Zuletzt geändert von Lori am Di 11. Mär 2014, 12:02, insgesamt 2-mal geändert.
Grund: richtigher Highlighter

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.4 FPC 3.2.2);

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

Re: Dynamische Arrays

Beitrag von wp_xyz »

Ich seh da nirgends, wo du das "array of TKabel" dimensionierst. Wenn das nicht geschehen ist, geht die Zuweisung "Value[a, ...] := ..." in TPlan.Getkabel natürlich schief.

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Dynamische Arrays

Beitrag von mschnell »

Michl hat geschrieben:Bei jedem SetLength von Length+1 muss das Betriebssystem einen neuen zusammenhängenden Speicherbereich bereitstellen.
Das glaube ich nicht.

Ich hab's nicht überprüft aber ich bin ziemlich sicher, dass die FPC RTL nicht so blöd ist und bei jeder mini-Erweiterung neuen Speicher anfordert (z.B. mit "realloc": die Daten müssen dann vom System ja möglicherweise auch dahin kopiert werden), sondern da wird eine Verwaltung sein, die mehr Speicher anlegt als aktuell gerade notwendig (z.B Vergrößerung auf mindestens 150 % und auf ganze Vielfache von 4K oder so).

-Michael

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Dynamische Arrays

Beitrag von mschnell »

Socke hat geschrieben:Dazu sagt die Doku etwas: http://www.freepascal.org/docs-html/pro ... 280008.4.1
Dynamisch Arrays verwenden natürlich den Heap, haben aber eine vorgeschaltete eigene Verwaltung.

Außerdem kann man alternative Heap-Verwaltungen an ein fpc-Projekt linken (z.B. cmem). Ich weiß, dass es zumindest früher in Delphi die Programm-Performace sehr steigern konnte, eine 3rd Party Heap-Verwaltung zu verwenden.

-Michael

Socke
Lazarusforum e. V.
Beiträge: 3178
Registriert: Di 22. Jul 2008, 19:27
OS, Lazarus, FPC: Lazarus: SVN; FPC: svn; Win 10/Linux/Raspbian/openSUSE
CPU-Target: 32bit x86 armhf
Wohnort: Köln
Kontaktdaten:

Re: Dynamische Arrays

Beitrag von Socke »

mschnell hat geschrieben:Ich hab's nicht überprüft aber ich bin ziemlich sicher, dass die FPC RTL nicht so blöd ist und bei jeder mini-Erweiterung neuen Speicher anfordert (z.B. mit "realloc": die Daten müssen dann vom System ja möglicherweise auch dahin kopiert werden), sondern da wird eine Verwaltung sein, die mehr Speicher anlegt als aktuell gerade notwendig (z.B Vergrößerung auf mindestens 150 % und auf ganze Vielfache von 4K oder so).
Bei der Funktion ReAllocMem steht: Die Adresse kann sich ändern. Es hängt also davon ab, ob am Ende des Arrays noch ausreichend ungenutzter Speicher vorhanden ist. Falls nicht, muss kopiert werden. Beim Anlegen eines Arrays wird jedoch nicht automatisch mehr Speicher allokiert als notwendig. Es wird lediglich auf ganze Blöcke (16 Byte auf 32bit-Systemen und 32 Byte auf 64bit-Systemen) aufgerundet.

An und für sich ist aber der Programmierer für die Reservierung von zusätzlichem Speicher verantwortlich. Ein schönes Beispeil sind die Standardklassen für Listen (TStringList, TFPList etc.) Dort wird zum Beispiel der verfügbare Platz immer verdoppelt.
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

Michl
Beiträge: 2511
Registriert: Di 19. Jun 2012, 12:54

Re: Dynamische Arrays

Beitrag von Michl »

@MSchnell: Meine Tipp beruhte auf eigene Erfahrung. Ich habe ein Programm, wo ich tatsächlich nicht weiss, wieviele Records in einem dynamischen Array im Programmverlauf benötigt werden. Ich konnte jedoch die Performance dieses um ein Mehrfaches steigern, indem ich bei einem SetLength(DynArray, Length + 1) zuvor (beim Create) ein SetLength(DynArray, vermutlich maximale Größe) gefolgt von einem SetLength(DynArray, 0) gesetzt hatte. Während des Programmverlaufs ändert sich scheinbar die Speicheradresse des dynamischen Arrays nicht mehr (solange die Größe des dynamischen Arrays nicht über die vermutete maximale Größe hinaus geht oder das Betriebssystem den freigegebenen Speicher wieder benötigt), sodaß sehr schnelle SetLength(DynArray, Length - 1) und SetLength(DynArray, Length + 1) möglich sind.

Dies ist jedoch nur eine praktische Beobachtung (analog Bsp. Mathias), die Erklärung hatte Socke ja schon geliefert.

Code: Alles auswählen

type
  TLiveSelection = (lsMoney, lsChilds, lsTime);
  TLive = Array[0..1] of TLiveSelection;  

Benutzeravatar
Maik81SE
Beiträge: 327
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.4 FPC 3.2.2)
CPU-Target: x86-64; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Dynamische Arrays

Beitrag von Maik81SE »

wp_xyz hat geschrieben:Ich seh da nirgends, wo du das "array of TKabel" dimensionierst. Wenn das nicht geschehen ist, geht die Zuweisung "Value[a, ...] := ..." in TPlan.Getkabel natürlich schief.
Weiß auf was du anspielst, aber wie ich auch in einigen Dokus gelesen habe gibt es einen Fehler, wenn ich eine Procedurevariable auf einen Wert festlegen will...

Muß ich mich dann nochmal in ruhe belesen, wenn ich den wieder finde...

Sonst muß ich versuchen das so zu setzten, das ich die "VARIABLE" GLOBAL für alle Form's freigebe und das wollte ich vermeiden.
Option B) auf 2k5 vordefinieren in der hoffnung, das es nie in der größe gebraucht wird. Ist ja nur die eine, die da probleme macht, da ich da ein Offenes Array übergeben muß...

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.4 FPC 3.2.2);

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

Re: Dynamische Arrays

Beitrag von wp_xyz »

Sorry, ich verstehe kein Wort, von dem was du hier sagst...

Du solltest hier die Deklaration von TPlan noch zeigen. Ich nehme an, dass TPlan ein Array von TKabel enthält (TPlan.fKabel) und dass diese Kabel in der Methode TPlan.GetKabel aus einer Datei eingelesen werden. Was soll da dieser Index? Ist das der Index eines Kabels? Wenn ja, aber warum ist Value dann ein Array of TKabel und kein TKabel allein?

Die folgende Variante deiner Methode "GetKabel" nimmt an, dass alle Kabeltypen in einer Datei stehen, und liest alle in das Array TPlan.FKabel ein.

Code: Alles auswählen

 
procedure TPlan.GetKabel;
var Temp : TStringlist;
    s: String;
    a: int64;  // warum nicht einfach nur "integer"?
begin
  fData.Filename := Format('%s', [fData.Geraet + '.vpl']);
  if FileExists(fData.Filename) then begin
    with TIniFile.Create(fData.Filename) do 
      try
        Temp := TStringList.Create;
        try 
          ReadSections(Temp);  // Hier stehen jetzt alle Schlüssel für die Kabel-Typen
          SetLength(fKabel, Temp.Count);
          for a:=0 to Temp.Count-1 do begin
            s := Temp[a];
            fKabel[a,0] := ReadString(s, 'Quelle', '');
            fKabel[a,1] := ReadString(s, 'Kontaktart-Quelle', '');
            // usw.
          end;
        finally
          Temp.Free;
        end;
      finally
        Free;
      end;
  end;
end;
 

Benutzeravatar
Maik81SE
Beiträge: 327
Registriert: Fr 30. Sep 2011, 14:07
OS, Lazarus, FPC: Debian 12 (L 3.4 FPC 3.2.2)
CPU-Target: x86-64; avr
Wohnort: Lübeck
Kontaktdaten:

Re: Dynamische Arrays

Beitrag von Maik81SE »

Moin,

Im Kern geb ich dir recht, nur muß ich dir in sofern wiedersprechen, das TPlan wie folgt ausschaut.

Code: Alles auswählen

  TPlan = class
    fKabel      : array of TKabel; //TKABEL: Array [0..7] of String;
    fData       : record
      Filename,
      Geraet    : String;
      index     : int64;
      end;
  private
    function    GetFile  : String;
    procedure   Setfile(Value: String);
  public
    constructor Create;
    destructor  Done;
    procedure   GetKabel(index: Int64; Value: array of TKabel);
    procedure   SetKabel(Value: TKabel);
    property    Kabel: TKabel {read fKabel }write SetKabel;
    property    _File: String read GetFile  write SetFile;
  end;
Ich weiß, da kann ich noch etwas umbauen, steht auch schon auf meiner ToDo...

Wo du aber voll in's schwarze getroffen hast, ist der Punkt, das ich aus einer File lese...

Bin sogar schon am Überlegen das alles Via Datenbanken zu machen nur habe ich da das Problem, das ich auf ACCESS angewiesen bin. Wie ich aber auch weiß geht das unter Lazarus wenn überhaupt nur Sehr schwer. Würde gerne Via SQL arbeiten, aber durch die Vorgaben von unseren Firmenserver habe ich meine Vorgaben. Das aber eine andere geschichte...

Zumindest kann ich nun die GetKabel ohne Parameter umschreiben.

Warum ich keine Integer verwende? Nun Punkt a) Negertiver wert, aber dafür kann man ja Word verwenden :D dann weiß ich heute noch nicht, wohin die reise gehen wird und mir somit die Möglichkeit eines Sehr großen Verdrahtungsplanes offen lassen will.

Code: Alles auswählen

label.caption:= 'gnublin.no-ip.info'
Debian 12 (L 3.4 FPC 3.2.2);

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

Re: Dynamische Arrays

Beitrag von wp_xyz »

Maik81SE hat geschrieben:die Möglichkeit eines Sehr großen Verdrahtungsplanes offen lassen
Na dann viel Spass beim Entwirren von mehr als 2.147.483.648 Kabeln, denn erst dann reicht ein Standard-32-bit Integer nicht mehr.

Spaß beiseite - was ich anders machen würde: Ich würde TKabel nicht stur als Array[0..7] of String deklarieren, weil, wie ich deinem 1. Code entnehme, auch numerische Daten enthalten sind. Da wäre ein Record viel klarer, auch weil du nicht immer nachschlagen musst, welche Größe jetzt das 3. Arrayelement bedeutet. Und vielleicht kannst du sowas wie "Leitungstyp" als eigenen Typ codieren, denn die Vielfalt eines Strings wird hier nicht auftreten (und jedes überflüssige Byte ist ein Hindernis für die Realisierung von mehr als 2.147.483.648 Kabeln - siehe oben). Dasselbe auch mit den Kontaktarten und Leitungsfarben.

Code: Alles auswählen

 
type
  TLeitungstyp = (ltSchutzleiter, ltNullLeiter, ltPhaseA, ltPhaseB, ltPhaseC, usw);
  TLeitungsfarbe = (lfSchwarz, lfBlau, lfBraun, lfGelbGruen, usw);
  TKontaktart = (--- hierzu fallen mir keine Beispiele ein ---);
  TKabel = record
     Quelle: String;
     KontaktartQuelle: TKontaktart;
     LeitungsTyp: TLeitungsTyp;
     LeitungsQuerschnitt: double;
     LeitungsLaenge: Double;
     Leitungsfarbe: TLeitungsfarbe;
     KontaktartZiel: TKontaktart;
     ZielKontakt: String;
  end;
 

mschnell
Beiträge: 3444
Registriert: Mo 11. Sep 2006, 10:24
OS, Lazarus, FPC: svn (Window32, Linux x64, Linux ARM (QNAP) (cross+nativ)
CPU-Target: X32 / X64 / ARMv5
Wohnort: Krefeld

Re: Dynamische Arrays

Beitrag von mschnell »

Michl hat geschrieben:Dies ist jedoch nur eine praktische Beobachtung
OK. Dann ist das System doch so dumm :o

Im Zweifelsfall sollte man also selbst einen Überbau zu Setlength machen, der mehr anlegt als unbedingt nötig (oder eben nichts, wenn's reicht). Wäre ein Dreizeiler.

-Michael

Antworten