Arrays und Zeiger

Für Fragen von Einsteigern und Programmieranfängern...
Antworten
Vbxler
Beiträge: 129
Registriert: Sa 25. Mai 2013, 07:43
OS, Lazarus, FPC: Win7_x64 (FPC:4.7.1)
CPU-Target: 32Bit

Arrays und Zeiger

Beitrag von Vbxler »

Hallo,
ich erforsche gerade Arrays und Zeiger mit folgendem code:

Code: Alles auswählen

 
unit Unit1;
{$mode objfpc}{$H+}
 
interface
uses
    Classes, SysUtils, FileUtil, Forms, Controls, Graphics, Dialogs, StdCtrls;
 
type
    PAdressRecord = ^TAdressRecord;
    TAdressRecord = record
        name    : string;
        ort     : string;
        plz     : integer;
  end;
 
 
    { TForm1 }
    TForm1 = class(TForm)
        cmdAuslesen: TButton;
        cmdLaden: TButton;
 
        procedure cmdLadenClick(Sender: TObject);
    private
        procedure DatenEintragen(pAdrRec: PAdressRecord);
        procedure Datenaendern(pAdrRec: PAdressRecord);
        procedure Datenlesen(pAdrRec: PAdressRecord);
    public
        { public declarations }
    end;
 
var
    Form1: TForm1;
 
implementation
{$R *.lfm}
{ TForm1 }
 
 
procedure TForm1.cmdLadenClick(Sender: TObject);
var
    aAdressen: Array of TAdressRecord;
    pAdressen: PAdressRecord;
begin
 
    SetLength(aAdressen, 2);
    pAdressen := @aAdressen[0];
 
    //Daten eintragen
    Datenlesen(pAdressen);
    DatenEintragen(pAdressen);
    Datenlesen(pAdressen);
    Datenaendern(pAdressen);
    Datenlesen(pAdressen);
end;
 
 
procedure TForm1.DatenEintragen(pAdrRec: PAdressRecord);
var
    iAnzahl     : Integer = 0;
begin
 
    pAdrRec[0].name     := 'Hans';
    pAdrRec[0].ort      := 'Berlin';
    pAdrRec[0].plz      := 1020;
 
    pAdrRec[1].name     := 'Peter';
    pAdrRec[1].ort      := 'Bonn';
    pAdrRec[1].plz      := 3020;
end;
 
 
procedure TForm1.Datenlesen(pAdrRec: PAdressRecord);
var
    sMeldung : string = '';
begin
 
    sMeldung:= pAdrRec[0].name + ';' + IntToStr(pAdrRec[0].plz) + ';' + pAdrRec[0].ort + #13 +
               pAdrRec[1].name + ';' + IntToStr(pAdrRec[1].plz) + ';' + pAdrRec[1].ort;
 
    ShowMessage(sMeldung);
end;
 
 
procedure TForm1.Datenaendern(pAdrRec: PAdressRecord);
var
    iAnzahl     : Integer = 0;
begin
 
    pAdrRec[0].plz:= 1022;
    pAdrRec[1].plz:= 3022;
end;
end.
 
Meine Fragen:
1) Wie kann man in der procedur 'Datenaendern' oder 'Datenlesen' die aktuellen Grenzen des Arrays aAdressen abfragen?
2) In der procedur 'cmdLadenClick' liegt das dyn. Array 'aAdressen' auf dem Stack. Wie kann man das machen, dass ich die
Daten des dyn. Array 'aAdressen' mit New() auf dem Heap liegen habe?


Danke für Eure Hilfe!


Vbxler
Vbxler
-------------------------

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: Arrays und Zeiger

Beitrag von Socke »

Vbxler hat geschrieben:Meine Fragen:
1) Wie kann man in der procedur 'Datenaendern' oder 'Datenlesen' die aktuellen Grenzen des Arrays aAdressen abfragen?
2) In der procedur 'cmdLadenClick' liegt das dyn. Array 'aAdressen' auf dem Stack. Wie kann man das machen, dass ich die
Daten des dyn. Array 'aAdressen' mit New() auf dem Heap liegen habe?
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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

Re: Arrays und Zeiger

Beitrag von Mathias »

Ich würde das Ganze ohne Pointer lösen, man kann dynamische Arrays ohne Probleme einer Procedure übergeben. Wen man die Array im Procedurekopf mit var deklariert, dann wird intern auch nur ein Zeiger übergeben.
Mit Lazarus sehe ich grün
Mit Java und C/C++ sehe ich rot

Vbxler
Beiträge: 129
Registriert: Sa 25. Mai 2013, 07:43
OS, Lazarus, FPC: Win7_x64 (FPC:4.7.1)
CPU-Target: 32Bit

Re: Arrays und Zeiger

Beitrag von Vbxler »

@Socke
zu 01: low und high habe ich versucht, geht aber leider nicht in den proceduren 'Datenaendern' oder 'Datenlesen', da aAdressen nicht bekannt ist.
Eventuell verhält es sich wie in C++, da geht das auch nicht, da ich ja nur einen Zeiger übergebe?
zu 02: Danke, das habe ich nicht gewusst, dass es im Heap erstellt wird.

@Mathias
Das mit der Referenz werde ich gleich mal prüfen.

Danke
Vbxler
-------------------------

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: Arrays und Zeiger

Beitrag von Socke »

Vbxler hat geschrieben:@Socke
zu 01: low und high habe ich versucht, geht aber leider nicht in den proceduren 'Datenaendern' oder 'Datenlesen', da aAdressen nicht bekannt ist.
Eventuell verhält es sich wie in C++, da geht das auch nicht, da ich ja nur einen Zeiger übergebe?
Dann hast du das gleiche Problem wie viele C-Entwickler. Dazu gibt es zwei Lösungen beziehungsweise Konventionen:
  • Du übergibst die Länge in einem zusätzlichen Parameter
  • Das letzte Element hat einen bestimmten Wert (in der Regel 0 oder nil)
Mathias hat geschrieben:Ich würde das Ganze ohne Pointer lösen, man kann dynamische Arrays ohne Probleme einer Procedure übergeben. Wen man die Array im Procedurekopf mit var deklariert, dann wird intern auch nur ein Zeiger übergeben.
Dynamische Arrays sind von sich aus nur Referenzen auf einen Speicherbereich im Heap. Bei einem Funktionsaufruf wird nicht der ganze Array auf dem Stack sondern nur dieser Zeiger übergeben.
Wenn man im Prozedurkopf den Array-Parameter mit var, const oder constref deklariert, bezieht sich das ausschließlich auf diesen Zeiger zum Array. Den Inhalt kann man immer ändern.

Hier ein kleines Testprogramm, in welchem das unterschiedliche Verhalten gezeigt wird.

Code: Alles auswählen

program Project1;
uses
  sysutils;
 
type
  Tarr = array of longint;
 
procedure ChangeArray(const b: TArr);
var
  l: Integer;
begin
  for l := low(b) to high(b) do
    b[l] := 5;  // Inhalt des Arrays wird verändert
end;
 
procedure AssignArray(var b: TArr);  // const -> Compilerfehler, ohne var -> Änderungen werden nicht nach Außen übernommen
var
  c: TArr;
begin
  setlength(c, 3);
  FillByte(c[0], length(c)*sizeof(c[0]), 3);
  b := c;  // Array selbst wird verändert
end;
 
procedure PrintArray(b: TArr);
var
  i: Integer;
begin
  // Ausgabe in Hexadezimal
  for i := low(b) to high(b) do
  begin
    write(IntToHex(b[i], sizeof(b[i])*2));
    if i < high(b) then
      write(',');
  end;
  writeln();
end;
 
var
  a: Tarr;
begin
  setlength(a, 5);
  Writeln('Adresse der Variable a   : ', PtrUInt(@a));
  Writeln('Erstes  Element      a[0]: ', PtrUint(@a[0]));
  Writeln('Letztes Element      a[4]: ', PtrUint(@a[4]));
  PrintArray(a);    // nach Setlength
  ChangeArray(a);
  PrintArray(a);    // nach Wertzuweisung
  AssignArray(a);
  PrintArray(a);    // nach Änderung
  readln;
end.
In der Ausgabe kann man wunderbar erkennen, dass die Variable a ganz wo anders liegt als die eigentlichen Daten:

Code: Alles auswählen

Adresse der Variable a   : 4288512
Erstes  Element      a[0]: 2580024
Letztes Element      a[4]: 2580040
00000000,00000000,00000000,00000000,00000000
00000005,00000005,00000005,00000005,00000005
03030303,03030303,03030303
MfG Socke
Ein Gedicht braucht keinen Reim//Ich pack’ hier trotzdem einen rein

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: Arrays und Zeiger

Beitrag von mschnell »

Ein Dynamisches Array ist (technisch) bereits ein Pointer (wie ein Objekt). Es braucht nicht als var oder const übergeben werden, um nicht alle Elemente zu kopieren. Dadurch wird natürlich bösartig verschleiert, das im Unterprogramm geäderte Array-Elemente dann im Hauptprogramm auch geändert sind, obwohl dieselbe Syntax (ohne var) wie bei einem Statischen Array (das ja beim Aufruf kopiert würde) verwendet wurde.

Wenn ich mich recht erinnere sollte High bei dynamischen Arrays nach der Übergabe korrekt definiert sein. Low ist bei dynamischen Arrays immer 0.

Das konzept der dynamischen Arrays ist zwar an sich OK, scheint an Objekt Pascal aber irgendwie hinten drangebastelt zu sein. :evil:

-Michael

Antworten