Logo

Marco Cantù's
Essential Pascal

Kapitel 8
Speicher

Anmerkung des Autors: Dieses Kapitel wird die Handhabung des Speichers behandeln, die verschiedenen Speicherbereiche diskutieren und eine Einführung in dynamische Arrays geben. Momentan ist nur der letztgenannte Teil verfügbar.

Delphi 4 dynamische Arrays

Historisch besitzt die Sprache Pascal immer nur Arrays (Felder) von fester Größe. Wenn Sie einen Datentyp unter Verwendung des Array-Konstrukts deklarieren, so müssen Sie auch die Anzahl der Elemente dieses Arrays spezifizieren. Wie die Experten unter den Programmierern wahrscheinlich wissen, gab es einige Techniken, die man anwenden konnte, um dynamische Arrays zu implementieren; typischerweise unter Verwendung von Pointern (Zeigern) und manueller Anforderung und Freigabe des erforderlichen Speichers.

Delphi 4 führt nun eine sehr einfache Implementation von dynamischen Arrays ein, indem diese analog den dynamischen langen Strings (die ich bereits behandelt habe) modelliert werden. Wie bei langen Strings werden dynamische Arrays dynamisch angelegt und ihre Referenzen gezählt, sie bieten aber keine Technik des Kopierens beim Beschreiben. Das ist aber kein großes Problem, da Sie ein Array freigeben können, indem Sie seine Variable auf nil setzen.

Sie können nun ein Array deklarieren, ohne die Anzahl seiner Elemente zu spezifizieren und es dann unter Verwendung der Prozedur SetLength mit einer bestimmten Größe anlegen. Die gleiche Prozedur kann auch verwendet werden, um ein Array ohne Verlust seines Inhalts in der Größe zu verändern. Es existieren noch weitere String-orientierte Prozeduren, wie z.B. die Copy-Funktion, die Sie auf Arrays anwenden können.

Hier ist ein kleiner Code-Ausschnitt, welches den Umstand betont, dass Sie für ein Array sowohl den Speicher deklarieren als auch anfordern müssen, bevor Sie mit seiner Benutzung beginnen können:

procedure TForm1.Button1Click(Sender: TObject);
var
  Array1: array of Integer;
begin
  Array1 [1] := 100; // Fehler
  SetLength (Array1, 100);
  Array1 [99] := 100; // OK
  ...
end;

Da Sie nur die Anzahl der Elemente eines Arrays festlegen können beginnt der Index unveränderlich bei 0. Konventionelle Arrays in Pascal besitzen eine untere Grenze ungleich 0 und auch einen nichtnumerischen Index, zwei Eigenschaften, die bei dynamischen Arrays nicht unterstützt werden. Um den Zustand eines dynamischen Arrays zu ermitteln, können Sie die Funktionen Length, High und Low verwenden, so wie bei jedem anderen Array auch. Bei dynamischen Arrays liefert Low immer den Wert 0 und High gibt die Länge minus 1 zurück. Das bedeutet, dass High für ein leeres Array den Wert -1 zurückliefert (wenn Sie darüber nachdenken ist das ein seltsamer Wert, da er niedriger ist, als der Rückgabewert von Low).

Abbildung 8.1: Das Formular des DynArr-Beispiels

Nach dieser kurzen Einleitung kann ich Ihnen ein einfaches Beispiel zeigen; es nennt sich DynArr und wird in Abbildung 8.1 dargestellt. Es ist daher einfach, da hier überhaupt keine komplexe Verwendung von dynamischen Arrays enthalten ist. Ich werde es auch benutzen, um einige mögliche Fehler zu zeigen, die der Programmierer machen kann. Das Programm vereinbart zwei globale Arrays und initialisiert das Erste in der Ereignisbehandlungsroutine OnCreate:

var
  Array1, Array2: array of Integer;

procedure TForm1.FormCreate(Sender: TObject);
begin
  // zuweisen
  SetLength (Array1, 100);
end;

Diese setzt alle Werte auf Null. Dieser Initialisierungscode ermöglicht es, sofort mit dem Lesen und Schreiben von Werten des Arrays zu beginnen, ohne jegliche Gefahr von Speicherfehlern. (Vorausgesetzt natürlich, Sie versuchen nicht auf Elemente zuzugreifen, die außerhalb der oberen Grenze des Arrays liegen.) Um eine noch bessere Initialisierung zu erzielen, besitzt das Programm einen Schalter, der in jede Zelle des Arrays einen Wert schreibt:

procedure TForm1.btnFillClick(Sender: TObject);
var
  I: Integer;
begin
  for I := Low (Array1) to High (Array1) do
    Array1 [I] := I;
end;

Der Schalter Grow gestattet Ihnen die Größe des Arrays zu verändern, ohne dabei dessen Inhalt zu verlieren. Das können Sie überprüfen, indem Sie den Schalter Get Value benutzen nachdem Sie den Grow-Schalter betätigt haben:

procedure TForm1.btnGrowClick(Sender: TObject);
begin
  // grow lässt die bestehenden Werte unberührt
  SetLength (Array1, 200);
end;

procedure TForm1.btnGetClick(Sender: TObject);
begin
  // anzeigen
  Caption := IntToStr (Array1 [99]);
end;

Der einzige etwas komplexere Code ist in dem OnClick Ereignis des Alias-Schalters enthalten. Das Programm kopiert mit dem := Operator ein Array in das andere, welches die Erzeugung eines Alias bewirkt (eine neue Variable verweist auf das gleiche Array im Speicher). Wenn Sie an dieser Stelle jedoch eines der Arrays modifizieren, so wird das andere ebenfalls betroffen sein, da sie beide auf den gleichen Speicherbereich verweisen:

procedure TForm1.btnAliasClick(Sender: TObject);
begin
  // Alias
  Array2 := Array1;
  // eins ändern (beide ändern sich)
  Array2 [99] := 1000;
  // den anderen anzeigen
  Caption := IntToStr (Array1 [99]);

Die Methode btnAliasClick führt noch zwei weitere Operationen durch. Die erste ist ein Test auf Gleichheit der beiden Arrays. Dieser prüft nicht die aktuellen Elemente der beiden Strukturen, sondern vergleicht nur die Speicherbereiche, auf die die beiden verweisen. Er prüft, ob die beiden Variablen zwei Aliase auf das selbe Array im Speicher sind:

procedure TForm1.btnAliasClick(Sender: TObject);
begin
  ...
  if Array1 = Array2 then
    Beep;
  // erstes Array kürzen
  Array1 := Copy (Array2, 0, 10);
end;

Die zweite Operation ist ein Aufruf der Copy Funktion, die nicht nur die Daten von einem Array in das andere verschiebt, sondern auch das erste Array mit einem neuen ersetzt, welches durch die Funktion erzeugt wurde. Die Auswirkung besteht darin, dass die Variable Array1 jetzt auf ein Array mit 11 Elementen verweist, so dass eine Betätigung der Schalter Get value und Set value einen Speicherfehler verursacht und eine Exception auslöst (es sei denn, Sie haben die Bereichsprüfung abgeschaltet; in diesem Fall erscheint der Fehler, aber es wird keine Exception angezeigt). Der Code des Schalters Fill arbeitet weiterhin korrekt, da die Anzahl der Elemente des zu verändernden Arrays unter Verwendung der aktuellen Grenzen bestimmt wird.

Zusammenfassung

Dieses Kapitel beinhaltet momentan nur dynamische Arrays, sicherlich ein wichtiges Element zur Speicherverwaltung, aber nur ein Teil des gesamten Bildes. Weiteres Material wird folgen.

Die Speicherstruktur, die in diesem Kapitel beschrieben wird, ist typisch für die Programmierung in Windows, ein Thema, in das ich im nächsten Kapitel einführen werde (ohne jedoch in voller Breite auf die Verwendung der VCL einzugehen).

Nächstes Kapitel: Windows-Programmierung

© Copyright Marco Cantù, Wintech Italia Srl 1995-2000
© Copyright der deutschen Übersetzung Immo Wache, 2000