Thema: Delphi Arrays

Einzelnen Beitrag anzeigen

choose

Registriert seit: 2. Nov 2003
Ort: Bei Kiel, SH
729 Beiträge
 
Delphi 2006 Architect
 
#12

Re: Arrays

  Alt 2. Nov 2003, 20:14
Hey Psycho,

Daten in einem Delphi Programm werden zZt entweder im Datensegment (globale Variablen), auf dem Stack (lokale Variablen, Rücksprungadressen und zT Parameter) oder auf dem sog. Heap (Objekte und Daten mit New, Getmem) abgelegt. Diese drei Bereiche sind allerdings in ihrer Struktur recht ähnlich: In ihnen deklarierte Variablen liegen linear im jeweiligen Speicherbereich hintereinander weg.

Deklarierst Du zB die beiden Variablen
Delphi-Quellcode:
var
  iFoo: Integer;
  iBar: Integer;
als globale Variablen, liegt die iFoo zB bei der Adresse @iFoo=$0044FC24 und iBar bei @iBar=$0044FC28 also bei @iFoo+4, vier Bytes "weiter hinten" im Speicher, hier dem Datensegment. Die vier Bytes entsprechen der Größe eines Integers unter Delphi.

Betrachtest Du nun ein Array, liegt jede Zelle des Arrays linear hinter ihrer Vorgängerzelle im Speicher. Bei dem Array arFoobar
Delphi-Quellcode:
var
  arFoobar: array[0..1] of Integer;
verhält es sich demnach genauso wie bei den beiden Variablen iFoo und iBar: Die Adresse der Zelle arFoobar[1] liegt vier Bytes "weiter hinten" als arFoobar[0].

Deklarierst Du nun Variablen hinter diesem Array, zB eine neue Variable iBarfoo, ebenfalls vom Typ Integer, würde der Code
arFoobar[2]:= 137; so übersetzt werden (dies ist in der Praxis nur mit einer Hilfsvariablen möglich, weil das Litaral 2 vom Compiler gegen die Arraygrenzen geprüft wird), dass die Adresse der Variablen iBarfoo berechnet und der Speicher an dieser Stelle manipuliert wird. In diesem Bsp ist also arFoobar[2] identisch mit iBarfoo!

Wenn Du Dir das Kompilat des Codes ansiehst, wirst Du erkennen, dass der Compiler lediglich Code generiert, der den Index (hier: 2) mit der Größe der Zelle des Arrays (hier: 4) multipliziert und zur Adresse der ersten Zelle des Arrays addiert (hier: @arFoobar=@arFoobar[0]).

Der Compiler kann die Größe des Datensegments zur Kompilierzeit berechnen und legt sie bei Delphiprogramm hart ins erstellte Binary ab. Das Betriebssystem kann deshalb ermitteln, ob ein Speicherzugriff gültig ist und löst andernfalls einen AccessVialation-Interrupt aus, der auf eine entspr. Delphi-Exception gemappt wird.
Der Heap eines Programms ist relativ dynamsch und wird unter Delphi mithilfe eines speziellen Speichermanagers verwaltet. Hier kannst Du Dich leider nicht darauf "verlassen", dass Du eine entsprechende Fehlermeldung bekommst, wenn Du bspw. einen Speicherbereich eines Objekts verwendest, das bereits freigegeben ist.
Der Stack besitzt bei Delphiprogrammen zwar eine feste größe, die ist allerdings so gewählt, das sie (hoffentlich) nie erreicht wird, andernfalls gäbe es einen StackOverflow. Stattdessen gibt es einen "aktuelle Bereich" (Stackframe, Stackpointer, Basepointer), an den geschrieben wird. An dieser Stelle ist das Schreiben über die zulässigen Grenzen hinaus sehr gefährlich, weil Du zB die mit obigen Code die Rücksprungadressen verändern würdest (der Stack ist TopDown organisiert)!

Allgemein lässt sich sagen, dass zwar interessante Dinge wie zB das lesen privater Exemplarvariablen, Vorinitialisieren von lokalen Variablen, Verbiegen von Rücksprungadressen, etc. mit dieser und ähnlichen Techniken möglich sind. Trotzdem geschehen solche Dinge idR eher unbewusst und können dann zu unerwünschten Erscheinungen führen. Darüber hinaus ist das bewusste Manipulieren von Daten stark vom Compiler abhängig (Data Word Size, Byte Alignment) und ist in einer VM (Managed Code in Delphi for .net) nicht möglich.

Stattdessen bietet der Compiler mit Range Checking {$R}, I/O Checking {$I} und Overflow Checking {$Q} brauchbare Hilfsmittel, um ungewolltes Verändern von Daten zu verhindern.

HTH
gruß, choose
  Mit Zitat antworten Zitat