Interne Datenformate (Delphi)

Aus RAD Studio
Wechseln zu: Navigation, Suche

Nach oben zu Speicherverwaltung - Index


In den folgenden Themen werden die internen Datenformate von Delphi-Datentypen beschrieben.

Integertypen

Integerwerte haben in Delphi die folgende interne Darstellung.

Plattformunabhängige vorzeichenlose Integertypen

Werte von plattformunabhängigen Integertypen belegen auf jeder Plattform dieselbe Bitanzahl.

Werte von vorzeichenlosen Integertypen sind immer positiv und haben kein Vorzeichenbit wie Integertypen mit Vorzeichen. Alle Bits von vorzeichenlosen Integertypen belegen die Größe des Wertes und haben keine andere Bedeutung.

Byte, UInt8

Byte und UInt8 sind 1 Byte (8 Bit) große vorzeichenlose positive Integerzahlen. Die Größe belegt alle 8 Bit.

Integer, 8 Bit ohne Vorzeichen

Word und UInt16

Word und UInt16 sind 2 Byte (16 Bit) große vorzeichenlose Integerzahlen.

Integer, 16 Bit ohne Vorzeichen

FixedUInt, Cardinal und UInt32

FixedUInt, Cardinal und UInt32 sind 4 Byte (32 Bit) große vorzeichenlose Integerzahlen.

Integer, 32 Bit ohne Vorzeichen

UInt64

UInt64 sind 8 Byte (64 Bit) große vorzeichenlose Integerzahlen.

Integer, 64 Bit ohne Vorzeichen

Plattformunabhängige Integertypen mit Vorzeichen

Werte von Integertypen mit Vorzeichen stellen das Vorzeichen einer Zahl durch ein führendes Vorzeichenbit dar, ausgedrückt durch das höchstwertige Bit. Das Vorzeichenbit ist für positive Zahlen 0 und für negative Zahlen 1. Die anderen Bits in einer positiven Integerzahl mit Vorzeichen werden von der Größe belegt. In einer negativen Integerzahl mit Vorzeichen werden die anderen Bits durch die 2er-Komplement-Darstellung der Größe des Wertes (absoluter Wert) belegt.

So ermitteln Sie das 2er-Komplement für eine Größe:

  1. Suchen Sie beginnend von rechts die erste "1".
  2. Invertieren Sie alle Bits links davon.

Zum Beispiel:

Beispiel 1 Beispiel 2
Größe 0101010 1010101

2er-Komplement

1010110 0101011

ShortInt, Int8

Shortint und Int8 sind 1 Byte (8 Bit) große Integerzahlen mit Vorzeichen. Das Vorzeichenbit belegt das höchstwertige siebte Bit, die Größe oder das 2er-Komplement belegt die anderen 7 Bit.

Integer, 8 Bit positiv mit Vorzeichen
Integer, 8 Bit negativ mit Vorzeichen

SmallInt und Int16

SmallInt und Int16 sind 2 Byte (16 Bit) große Integerzahlen mit Vorzeichen.

Integer, 16 Bit positiv mit Vorzeichen
Integer, 16 Bit negativ mit Vorzeichen

FixedInt, Integer und Int32

FixedInt, Integer und Int32 sind 4 Byte (32 Bit) große Integerzahlen mit Vorzeichen.

Integer, 32 Bit positiv mit Vorzeichen
Integer, 32 Bit negativ mit Vorzeichen

Int64

Int64 sind 8 Byte (64 Bit) große Integerzahlen mit Vorzeichen.

Integer, 64 Bit positiv mit Vorzeichen
Integer, 64 Bit negativ mit Vorzeichen

Plattformabhängige Integertypen

Die plattformabhängigen Integertypen werden konvertiert, damit sie zu der Bitgröße der aktuellen Zielplattform passen. Auf 64-Bit-Plattformen belegen Sie 64 Bit, auf 32-Bit-Plattformen 32 Bit (mit Ausnahme der Typen LongInt und LongWord). Wenn die Größe der Zielplattform und die Größe der CPU-Plattform identisch sind, dann stimmt eine plattformabhängige Integerzahl exakt mit der Größe der CPU-Register überein. Diese Typen werden häufig verwendet, wenn eine bestmögliche Leistung für einen bestimmten CPU-Typ und ein bestimmtes Betriebssystem erwünscht ist.

Vorzeichenloser NativeUInt-Integer

NativeUInt ist der plattformabhängige vorzeichenlose Integertyp. Die Größe und interne Darstellung von NativeUInt hängen von der aktuellen Plattform ab. Auf 32-Bit-Plattformen entspricht NativeUInt dem Typ Cardinal. Auf 64-Bit-Plattformen entspricht NativeUInt dem Typ UInt64.

NativeInt-Integer mit Vorzeichen

NativeInt ist der plattformabhängige Integertyp mit Vorzeichen. Die Größe und interne Darstellung von NativeInt hängen von der aktuellen Plattform ab. Auf 32-Bit-Plattformen entspricht NativeInt dem Typ Integer. Auf 64-Bit-Plattformen entspricht NativeInt dem Typ Int64.

LongInt und LongWord

LongInt definiert den Integertyp mit Vorzeichen und LongWord den Integertyp ohne Vorzeichen. Die Größe der plattformabhängigen Integertypen LongInt und LongWord ist auf jeder Plattform anders, mit Ausnahme von 64-Bit-Windows; hier bleibt die Größe unverändert (32 Bit).

Größe
32-Bit-Plattformen und 64-Bit-Windows-Plattformen 64-Bit-iOS-Plattformen
LongInt 32 Bit (4 Byte) 64 Bit (8 Byte)

LongWord

32 Bit (4 Byte) 64 Bit (8 Byte)
Hinweis:
Zu 32-Bit-Plattformen gehören in RAD Studio 32-Bit-Windows, OSX32, 32-Bit-iOS und Android.

Verwenden Sie auf 64-Bit-iOS-Plattformen:

Integer-Teilbereichstypen

Wenn Sie mit Integerkonstanten die Ober- und Unterbereichsgrenzen von Teilbereichstypen festlegen, definieren Sie einen Integer-Teilbereichstyp. Ein Integer-Teilbereich ist eine Untermenge der Werte eines anderen Integertyps (des sogenannten Basistyps). Der Basistyp ist der kleinste Integertyp, der den angegebenen Bereich enthält (enthält die Ober- und Unterbereichsgrenzen).

Das interne Datenformat, in dem eine Variable eines Integer-Teilbereichstyps dargestellt werden kann, hängt von den Bereichsgrenzen ab:

  • Liegen beide Grenzen im Bereich -128..127 (ShortInt), wird die Variable als ein Byte mit Vorzeichen gespeichert.
  • Liegen beide Grenzen im Bereich 0..255 (Byte), wird die Variable als ein Byte ohne Vorzeichen gespeichert.
  • Liegen beide Grenzen im Bereich -32768..32767 (SmallInt), wird die Variable als ein Word mit Vorzeichen gespeichert.
  • Liegen beide Grenzen im Bereich 0..65535 (Word), wird die Variable als ein Word ohne Vorzeichen gespeichert.
  • Liegen beide Grenzen im Bereich -2147483648..2147483647 (FixedInt und LongInt auf 32-Bit-Plattformen und 64-Bit-Windows-Plattformen), wird die Variable als ein Double Word mit Vorzeichen gespeichert.
  • Liegen beide Grenzen im Bereich 0..4294967295 (FixedUInt und LongWord auf 32-Bit-Plattformen und 64-Bit-Windows-Plattformen), wird die Variable als ein Double Word ohne Vorzeichen gespeichert.
  • Liegen beide Grenzen im Bereich -2^63..2^63-1 (Int64 und LongInt auf 64-Bit-iOS-Plattformen), wird die Variable als Quadruple Word mit Vorzeichen gespeichert.
  • Liegen beide Grenzen im Bereich 0..2^64-1 (UInt64 und LongWord auf 64-Bit-iOS-Plattformen), wird die Variable als Quadruple Word ohne Vorzeichen gespeichert.

Hinweis: Ein "Word" belegt zwei Byte.

Zeichentypen

Auf 32-Bit- und 64-Bit-Plattformen:

  • Char und WideChar werden als Word-Variable ohne Vorzeichen in der Regel in der Codierung UTF-16 oder Unicode gespeichert.
  • AnsiChar wird als Byte ohne Vorzeichen gespeichert. In Delphi 2007 und früher wurde Char als ein AnsiChar dargestellt. Der Zeichentyp für kurze Strings ist immer AnsiChar. Er wird in vorzeichenlosen Byte-Werten gespeichert.
  • Der Standardtyp für lange Strings (string) ist jetzt UnicodeString, dessen Referenzzählung der von AnsiString entspricht. AnsiString war früher der Standardtyp für lange Strings. Um die Kompatibilität mit älterem Quelltext aufrechtzuerhalten, könnte die Verwendung des Typs AnsiString erforderlich sein.

Boolesche Typen

Ein Boolean-Typ wird als ein Byte, ein ByteBool-Typ wird als ein Byte, ein WordBool-Typ wird als ein Word und ein LongBool-Typ wird als ein Longint gespeichert.

Ein Boolean-Typ kann die Werte 0 (False) und 1 (True) annehmen. Die Typen ByteBool, WordBool und LongBool können die Werte 0 (False) oder ungleich 0 (True) annehmen.

Aufzählungstypen

Ein Aufzählungstyp wird als ein Byte ohne Vorzeichen gespeichert, wenn die Aufzählung nicht mehr als 256 Werte umfasst und der Typ im Status {$Z1} (Voreinstellung) deklariert wurde. Enthält der Aufzählungstyp mehr als 256 Werte oder wurde er im Status {$Z2} deklariert, wird er als ein Word ohne Vorzeichen gespeichert. Wird ein Aufzählungstyp im Status {$Z4} deklariert, wird er als ein Double Word ohne Vorzeichen gespeichert.

Reelle Typen

Reelle Typen speichern die binäre Entsprechung des Vorzeichens (+ oder -), eines Exponenten und einer Mantisse. Ein reeller Wert wird in der folgenden Form dargestellt:

+/- significand * 2^exponent

Dabei wird für die Mantisse links vom binären Dezimalpunkt ein einziges Bit verwendet (das heißt, 0 <= Mantisse < 2).

In den folgenden Abbildungen befindet sich das höchstwertige Bit immer auf der linken Seite, das niedrigstwertige Bit immer auf der rechten Seite. Die Zahlen am oberen Rand geben die Breite jedes Feldes in Bit an. Die Elemente ganz links werden an den höchsten Adressen gespeichert. Bei einem Real48-Wert wird beispielsweise e im ersten Byte, f in den nächsten fünf Byte und s im höchstwertigen Bit des letzten Byte gespeichert.

Der Typ Real48

Auf 32-Bit- und 64-Bit-Plattform wird eine Real48-Zahl mit 6 Byte (48 Bit) in drei Felder unterteilt:

1

           39                                 

     8     

s

          f

    e


Wenn gilt 0 < e <= 255, ergibt sich der Wert v der Zahl folgendermaßen:

v = (-1)s * 2(e-129) * (1.f)

Wenn e = 0, ist v = 0.

Der Typ Real48 eignet sich nicht zum Speichern von denormalisierten Werten, NaN-Werten (Not a Number = keine Zahl) und unendlichen Werten. Denormalisierte Zahlen werden zu null, wenn sie als Real48 gespeichert werden, und das Speichern von NaN- und unendlichen Werten in Real48-Variablen führt zu einem Überlauffehler.

Der Typ Single

Auf 32-Bit- und 64-Bit-Plattform wird eine Single-Zahl mit 4 Byte (32 Bit) in drei Felder unterteilt:

1

     8     

           23           

s

     e

           f


Der Wert v der Zahl ergibt sich folgendermaßen:

  • Wenn 0 < e < 255, dann ist v = (-1)s * 2(e-127) * (1.f)
  • Wenn e = 0 und f <> 0, dann ist v = (-1)s * 2(-126) * (0.f)
  • Wenn e = 0 und f = 0, dann ist v = (-1)s * 0
  • Wenn e = 255 und f = 0, dann ist v = (-1)s * Inf
  • Wenn e = 255 und f <> 0, dann ist v ein NaN-Wert

Der Typ Double

Der generische Typ Real entspricht in seiner aktuellen Implementierung dem Typ Double.

Auf 32-Bit- und 64-Bit-Plattform wird eine Double-Zahl mit 8 Byte (64 Bit) in drei Felder unterteilt:

1

      11      

                           52                           

s

      e

                           f


Der Wert v der Zahl ergibt sich folgendermaßen:

  • Wenn 0 < e < 2047, dann ist v = (-1)s * 2(e-1023) * (1.f)
  • Wenn e = 0 und f <> 0, dann ist v = (-1)s * 2(-1022) * (0.f)
  • Wenn e = 0 und f = 0, dann ist v = (-1)s * 0
  • Wenn e = 2047 und f = 0, dann ist v = (-1)s * Inf
  • Wenn e = 2047 und f <> 0, dann ist v ein NaN-Wert

Der Typ Extended

Auf der 64-Bit-Intel-Plattform und ARM-Platform ist der Typ Extended ein Alias für den Typ Double, der nur 8 Byte groß ist. (Siehe den Abschnitt Der Typ Double weiter oben.) Weitere Informationen finden Sie unter Gesichtspunkte für geräteübergreifende Delphi-Anwendungen.

Auf der 32-Bit-Intel-Plattformen wird eine Extended-Zahl mit 10 Byte (80 Bit) dargestellt. Eine Extended-Zahl ist in vier Felder unterteilt.

1

         15         

1

                                  63                                  

s

         e

i

                                  f


Der Wert v der Zahl ergibt sich folgendermaßen:

  • Wenn 0 <= e < 32767, dann ist v = (-1)s * 2(e-16383) * (i.f)
  • Wenn e = 32767 und f = 0, dann ist v = (-1)s * Inf
  • Wenn e = 32767 und f <> 0, dann ist v ein NaN-Wert

Der Typ Comp

Eine Comp-Zahl mit 8 Byte (64 Bit) wird als 64-Bit-Integer mit Vorzeichen gespeichert.

Der Typ Currency

Eine Currency-Zahl mit 8 Byte (64 Bit) wird als skalierter 64-Bit-Integer mit Vorzeichen gespeichert. Dabei stehen die 4 niedrigstwertigen Ziffern implizit für 4 Dezimalstellen.

Zeigertypen

Auf 32-Bit-Plattformen wird ein Zeigertyp in 4 Byte als 32-Bit-Adresse gespeichert.

Auf 64-Bit-Plattformen wird ein Zeigertyp in 8 Byte als 64-Bit-Adresse gespeichert.

Der Zeigerwert nil wird als null gespeichert.

Kurze String-Typen

Die Anzahl der Bytes eines ShortString-Strings ergibt sich aus seiner maximalen Länge + 1. Das erste Byte enthält die aktuelle dynamische Länge des Strings, die folgenden Bytes seine Zeichen.

Das Längenbyte und die Zeichen werden als Werte ohne Vorzeichen betrachtet. Die maximale Länge eines Strings beträgt 255 Zeichen plus ein Längenbyte (string[255]).

Lange String-Typen

Eine String-Variable vom Typ UnicodeString oder AnsiString belegt auf 32-Bit-Plattformen 4 Byte (und auf 64-Bit 8 Byte) im Arbeitsspeicher, die einen Zeiger auf einen dynamisch zugewiesenen String enthalten. Wenn eine String-Variable leer ist (also einen String der Länge 0 enthält), ist der String-Zeiger nil, und der String-Variable wird kein dynamischer Speicher zugewiesen. Andernfalls referenziert der String-Zeiger einen dynamisch zugewiesenen Speicherblock, der neben dem String-Wert Informationen zur Beschreibung des Strings enthält. Die nachfolgenden Tabellen zeigen den Aufbau eines Speicherblocks für einen langen String.

Format des Datentyps UnicodeString (32 Bit und 64 Bit)

Feld Codeseite Elementgröße Referenzzählung Länge String-Daten

(Elementgröße)

Nullterminiert

Offset

-12

-10

-8

-4

0..(Länge - 1)

Länge * Elementgröße

Inhalt

16-Bit-Codeseite der String-Daten

16-Bit-Elementgröße der String-Daten

32-Bit-Referenzzählung

Länge in Zeichen

Zeichen-String mit Daten nach Größe der Elemente

NULL-Zeichen

Die Zahlen in der Zeile Offset stellen die Offsets der Felder dar, welche den String-Inhalt ab dem String-Zeiger beschreiben, der auf das Feld String-Daten zeigt (Offset = 0), das einen Speicherblock mit den tatsächlichen String-Werten enthält.

Das NULL-Zeichen am Ende des Speicherblocks eines Strings wird vom Compiler und den integrierten String-Routinen automatisch verwaltet. Durch das NULL-Zeichen kann ein String direkt in einen nullterminierten String umgewandelt werden.

Siehe auch Neuer String-Typ: UnicodeString.

Für String-Literale erzeugt der Compiler einen Speicherblock mit demselben Aufbau wie bei einem dynamisch zugewiesenen String, aber mit einem Referenzzähler von -1. String-Konstanten werden genauso behandelt, der einzige Unterschied zu Literalen ist, dass eine String-Konstante ein Zeiger auf einen -1-Referenzzählerblock ist.

Wenn ein Zeiger auf eine String-Struktur (Quelle) einer String-Variable (Ziel) zugewiesen wird, schreibt der Referenzzähler vor, wie dies ausgeführt wird. Normalerweise wird der Referenzzähler für das Ziel erhöht und für die Quelle verringert, da beide Zeiger (Quelle und Ziel) nach der Zuweisung auf denselben Speicherblock zeigen.

Wenn der Referenzzähler der Quelle -1 (String-Konstante) ist, wird eine neue Struktur mit einem Referenzzähler von 1 erstellt. Wenn das Ziel nicht nil ist, wird der Referenzzähler verringert. Wenn er 0 erreicht, wird die Struktur aus dem Speicher freigegeben. Wenn das Ziel nil ist, werden keine weiteren Aktionen dafür ausgeführt. Das Ziel zeigt dann auf die neue Struktur.

var
 destination : String;
 source : String;
...
destination := 'qwerty';  // reference count for the newly-created block of memory (containing the 'qwerty' string) pointed at by the "destination" variable is now 1
...
source := 'asdfgh'; // reference count for the newly-created block of memory (containing the 'asdfgh' string) pointed at by the "destination" variable is now 1
destination := source; // reference count for the memory block containing the 'asdfgh' string is now 2, and since reference count for the block of memory containing the 'qwerty' string is now 0, the memory block is deallocated.

Wenn der Referenzzähler für die Quelle nicht -1 ist, wird er nicht erhöht, und das Ziel zeigt darauf.

var
  destination, destination2, destination3: String;
  destination := 'Sample String'; //reference count for the newly-created block of memory containing 'Sample string' is 1.
  destination2 := destination; //reference count for the block of memory containing 'Sample string' is now 2.
  destination3 := destination; //reference count for the block of memory containing 'Sample string' is now 3.

Hinweis: String-Variablen können nicht auf eine Struktur mit einer Referenzzählung von 0 zeigen. Strukturen werden immer freigegeben, wenn sie die Referenzzählung 0 erreichen, und sie können nicht geändert werden, wenn ihre Referenzzählung -1 ist.

WideString-Typen

Auf 32-Bit-Plattformen belegt eine WideString-Variable 4 Byte (und auf 64-Bit-Plattformen 8 Byte), die einen Zeiger auf einen dynamisch zugewiesenen String enthalten. Wenn eine WideString-Variable leer ist (also einen String der Länge 0 enthält), ist der String-Zeiger nil, und der Variable wird kein dynamischer Speicher zugewiesen. Andernfalls referenziert der String-Zeiger einen dynamisch zugewiesenen Speicherblock, der neben dem String-Wert eine Längenangabe von 32 Bit enthält. Die nachstehende Tabelle zeigt den Aufbau eines Speicherblocks für einen WideString in Windows.

Aufbau des Speichers für einen WideString (32 Bit und 64 Bit)

Offset

-4

0..(Länge - 1)

Länge

Inhalt

32-Bit-Längenindikator
(in Byte)

Zeichen-String

NULL-Zeichen

Die Länge des Strings ergibt sich aus der Anzahl der Bytes und ist damit doppelt so groß wie die Anzahl der Wide-Zeichen, die er enthält.

Das NULL-Zeichen am Ende des Speicherblocks eines WideStrings wird vom Compiler und den integrierten String-Routinen automatisch verwaltet. Durch das NULL-Zeichen kann ein WideString direkt in einen nullterminierten String umgewandelt werden.

Mengentypen

Eine Menge ist ein Array von Bits. Jedes Bit zeigt an, ob ein Element in der Menge enthalten ist oder nicht. Da die maximale Anzahl der Elemente einer Menge 256 beträgt, belegt eine Menge nie mehr als 32 Byte. Die Anzahl der Bytes, die eine bestimmte Menge belegt, ergibt sich wie folgt:

(Max div 8) - (Min div 8) + 1

wobei Max und Min die obere und die untere Grenze des Basistyps der Menge sind. Die Byte-Anzahl eines bestimmten Elements E ergibt sich wie folgt:

(E div 8) - (Min div 8)

und die Bitanzahl in diesem Byte ergibt sich aus

E mod 8

wobei E der ordinale Wert des Elements ist. Der Compiler speichert Mengen nach Möglichkeit in CPU-Registern. Eine Menge bleibt jedoch immer im Speicher, wenn sie größer als der plattformabhängige Integertyp ist oder im Quelltext des Programms auf die Adresse der Menge zugegriffen wird.

Statische Array-Typen

Auf der 32-Bit-Plattform belegt eine statische Array-Variable 4 Byte im Speicher (und 8 Byte auf der 64-Bit-Plattform), die einen Zeiger auf ein statisch zugewiesenes Array enthalten. Ein statisches Array wird als fortlaufende Folge von Variablen des Komponententyps des Arrays gespeichert. Die Komponenten mit den niedrigsten Indizes werden an der niedrigsten Speicheradresse abgelegt. Bei einem mehrdimensionalen Array liegt die äußerste rechte Dimension an der Basis des belegten Speicherblocks.

Dynamische Array-Typen

Auf der 32-Bit-Plattform belegt eine dynamische Array-Variable 4 Byte im Speicher (und 8 Byte auf der 64-Bit-Plattform), die einen Zeiger auf ein dynamisch zugewiesenes Array enthalten. Ist eine Variable leer (also nicht initialisiert), ist der Zeiger nil, und der Variable wird kein dynamischer Speicher zugewiesen. Andernfalls referenziert die Variable einen dynamisch zugewiesenen Speicherblock, der neben dem Array eine Längenangabe von 32 Bit (unter Win64 von 64 Bit) und einen Referenzzähler von 32 Bit enthält. Die nachstehende Tabelle zeigt den Aufbau eines Speicherblocks für ein dynamisches Array.

Aufbau des Speichers für ein dynamisches Array (32 Bit und 64 Bit)

Offset 32 Bit

-8

-4

0..(Länge * Größe_des_Elements - 1)

Offset 64 Bit

-12

-8

0..(Länge * Größe_des_Elements - 1)

Inhalt

32-Bit-Referenzzählung

32 Bit oder 64 Bit auf 64-Bit-Plattformen
Längenindikator
(Anzahl der Elemente)

Array-Elemente

Record-Typen

Wenn ein Record-Typ mit dem Status {$A+} (Vorgabe) deklariert wird und die Deklaration nicht den Modifizierer packed enthält, handelt es sich um einen ungepackten Record-Typ. Die Felder des Records werden so ausgerichtet, dass die CPU entsprechend der jeweiligen Plattform möglichst effizient darauf zugreifen kann. Die Ausrichtung wird vom Typ des jeweiligen Feldes gesteuert. Jeder Datentyp besitzt eine implizite Ausrichtungsmaske, die vom Compiler automatisch berechnet wird. Sie kann die Werte 1, 2, 4 oder 8 haben und entspricht dem Byte-Raster, in dem ein Wert dieses Typs für den optimalen Zugriff im Speicher angeordnet werden muss. Die folgende Tabelle enthält die Ausrichtungsmasken für alle Datentypen.

Ausrichtungsmasken für Typen (nur 32 Bit)

Typ Ausrichtung

Ordinale Typen

Größe des Typs (1, 2, 4 oder 8)

Reelle Typen

2 für Real48, 4 für Single, 8 für Double und Extended

Kurze String-Typen

1

Array-Typen

Entspricht dem Typ der Array-Elemente

Record-Typen

Die größte Ausrichtungsmaske der Felder im Record

Mengentypen

Größe des Typs bei 1, 2 oder 4, andernfalls 1

Alle anderen Typen

Werden von der $A-Direktive festgelegt


Um die korrekte Ausrichtung der Felder in einem ungepackten Record-Typ zu gewährleisten, fügt der Compiler vor den Feldern mit der Ausrichtungsmaske 2 ein leeres Byte ein. Bei Feldern mit der Ausrichtungsmaske 4 werden nach Bedarf bis zu 3 leere Byte eingefügt. Schließlich erweitert der Compiler die gesamte Größe des Records bis zu der Byte-Grenze, die sich aus der größten Ausrichtungsmaske der enthaltenen Felder ergibt.

Implizites Packen von Feldern mit einer gemeinsamen Typspezifikation

In früheren Versionen des Delphi-Compilers, wie Delphi 7 und vorher, wurde die Ausrichtung packed implizit für Felder angewendet, die gemeinsam deklariert wurden, d. h., für Felder, die eine gemeinsame Typspezifikation haben. Neuere Compiler können dieses Verhalten erzeugen, wenn Sie die Direktive {$OLDTYPELAYOUT ON} angeben. Diese Direktive packt (auf Byte-Basis) die Felder mit einer gemeinsamen Typspezifikation, auch wenn in der Deklaration der Modifizierer packed nicht enthalten ist und der Record-Typ nicht mit dem Status {$A-} deklariert wurde.

Betrachten Sie folgende Deklaration:

 {$OLDTYPELAYOUT ON}
 type
   TMyRecord = record
     A, B: Extended;
     C: Extended;
   end;
 {$OLDTYPELAYOUT OFF}

A und B werden gepackt (an Byte-Grenzen ausgerichtet), weil die Direktive {$OLDTYPELAYOUT ON} angegeben ist und A und B dieselbe Typspezifikation verwenden. Für das separat deklarierte Feld C verwendet der Compiler aber das Standardverhalten und füllt die Struktur mit unbenutzten Bytes auf, damit das Feld an einer Quadword-Grenze erscheint.

Wenn ein Record-Typ mit dem Status {$A-} deklariert wird oder die Deklaration den Modifizierer packed enthält, werden die Felder des Records nicht ausgerichtet, sondern an aufeinander folgenden Offsets abgelegt. Die Gesamtgröße eines solchen gepackten Records ergibt sich aus der Größe aller Felder. Da sich die Datenausrichtung ändern kann, bieten sich gepackte Record-Typen für alle Record-Strukturen an, die auf die Festplatte geschrieben oder im Speicher an ein Modul übergeben werden sollen, das mit einer anderen Version des Compilers compiliert wird.

Dateitypen

Dateitypen werden als Records dargestellt. Typisierte und untypisierte Dateien belegen auf 32-Bit-Plattformen 592 Byte und auf 64-Bit-Plattformen 616 Byte, die sich wie folgt verteilen:

 type
   TFileRec = packed record
     Handle: NativeInt;
     Mode: word;
     Flags: word;
     case Byte of
       0: (RecSize: Cardinal);
       1: (BufSize: Cardinal;
    	   BufPos: Cardinal;
    	   BufEnd: Cardinal;
    	   BufPtr: _PAnsiChr;
    	   OpenFunc: Pointer;
    	   InOutFunc: Pointer;
    	   FlushFunc: Pointer;
    	   CloseFunc: Pointer;
    	   UserData: array[1..32] of Byte;
    	   Name: array[0..259] of WideChar; );
  end;

Textdateien belegen auf 32-Bit-Plattformen 730 Byte und auf 64-Bit-Plattformen 754 Byte, die sich wie folgt verteilen:

 type
   TTextBuf = array[0..127] of Char;
   TTextRec = packed record
     Handle: NativeInt;
     Mode: word;
     Flags: word;
     BufSize: Cardinal;
     BufPos: Cardinal;
     BufEnd: Cardinal;
     BufPtr: _PAnsiChr;
     OpenFunc: Pointer;
     InOutFunc: Pointer;
     FlushFunc: Pointer;
     CloseFunc: Pointer;
     UserData: array[1..32] of Byte;
     Name: array[0..259] of WideChar;
     Buffer: TTextBuf; //
     CodePage: Word;
     MBCSLength: ShortInt;
     MBCSBufPos: Byte;
     case Integer of
       0: (MBCSBuffer: array[0..5] of _AnsiChr);
       1: (UTF16Buffer: array[0..2] of WideChar);
   end;

Handle enthält das Handle der Datei (wenn die Datei geöffnet ist).

Das Feld Mode kann einen der folgenden Werte annehmen:

 const
   fmClosed = $D7B0;
   fmInput= $D7B1;
   fmOutput = $D7B2;
   fmInOut= $D7B3;

fmClosed gibt dabei an, dass die Datei geschlossen ist. fmInput und fmOutput geben an, dass es sich bei der Datei um eine Textdatei handelt, die zurückgesetzt (fmInput) oder neu geschrieben (fmOutput) wurde. fmInOut gibt an, dass die Datei eine typisierte oder eine untypisierte Datei ist, die zurückgesetzt oder neu geschrieben wurde. Jeder andere Wert gibt an, dass die Dateivariable nicht zugeordnet (und damit nicht initialisiert) wurde.

Das Feld UserData wird für benutzerdefinierte Routinen freigehalten, um Daten zu speichern.

Name enthält den Dateinamen. Dieser besteht aus einer Folge von Zeichen, die mit einem Null-Zeichen (#0) abgeschlossen ist.

Bei typisierten und untypisierten Dateien enthält RecSize die Record-Länge in Byte. Das Feld Private ist reserviert und wird nicht verwendet.

Bei Textdateien ist BufPtr ein Zeiger auf einen Puffer mit BufSize Byte, BufPos ist der Index des nächsten zu lesenden oder zu schreibenden Zeichens des Puffers. BufEnd entspricht der Anzahl der gültigen Zeichen im Puffer. OpenFunc, InOutFunc, FlushFunc und CloseFunc sind Zeiger auf die E/A-Routinen, die die Datei verwalten. Weitere Informationen zu diesem Thema finden Sie unter "Gerätefunktionen". Flags legt die Art des Zeilenumbruchs fest.

Bit 0 nicht gesetzt

LF-Zeilenumbruch

Bit 0 gesetzt

CRLF-Zeilenumbruch

Alle anderen Flags-Bits sind für die zukünftige Verwendung reserviert.

Hinweis: Für den Typ UnicodeString (dem Standard-String-Typ in Delphi) sind die verschiedenen Stream-Typen aus der Unit Classes (TFileStream, TStreamReader, TStreamWriter usw.) nützlicher, weil die älteren Dateitypen (besonders ältere Textdateitypen) nur über eine begrenzte Unicode-Funktionalität verfügen.

Prozedurale Typen

Auf der 32-Bit-Plattform wird ein Prozedurzeiger als 32-Bit-Zeiger auf den Eintrittspunkt einer Prozedur oder Funktion gespeichert. Ein Methodenzeiger wird als 32-Bit-Zeiger auf den Eintrittspunkt einer Methode gespeichert, dem ein 32-Bit-Zeiger auf ein Objekt folgt.

Auf der 64-Bit-Plattform wird ein Prozedurzeiger als 64-Bit-Zeiger auf den Eintrittspunkt einer Prozedur oder Funktion gespeichert. Ein Methodenzeiger wird als 64-Bit-Zeiger auf den Eintrittspunkt einer Methode gespeichert, dem ein 64-Bit-Zeiger auf ein Objekt folgt.

Klassentypen

Auf der 32-Bit-Plattform (Win32, OSX, iOS und Android) wird der Wert eines Klassentyps als 32-Bit-Zeiger auf eine Instanz der Klasse (bzw. als 64-Bit-Zeiger auf der 64-Bit-Plattform) gespeichert. Die Instanz einer Klasse wird auch als Objekt bezeichnet. Das interne Datenformat eines Objekts gleicht dem eines Records. Die Felder eines Objekts werden in der Reihenfolge ihrer Deklaration als fortlaufende Folge von Variablen gespeichert. Die Felder werden wie bei einem ungepackten Record-Typ immer ausgerichtet. Die Ausrichtung entspricht deshalb der größten Ausrichtungsmaske der Felder im Objekt. Alle von einer Vorfahrklasse geerbten Felder werden vor den neuen Feldern gespeichert, die in der abgeleiteten Klasse definiert wurden.

Auf der 32-Bit-Plattform ist das erste, 4 Byte große Feld aller Objekte (bzw. auf der 64-Bit-Plattform das erste, 8 Byte große Feld) ein Zeiger auf die virtuelle Methodentabelle (VMT) der Klasse. Es gibt nur eine VMT pro Klasse (und nicht eine für jedes Objekt). Verschiedene Klassentypen verwenden nie dieselbe VMT. VMTs werden automatisch vom Compiler erstellt und nie direkt von einem Programm geändert. Ebenso werden die Zeiger auf VMTs automatisch von den Konstruktormethoden in den erstellten Objekten gespeichert und nie direkt von einem Programm geändert.

Die folgende Tabelle zeigt die Struktur einer VMT. Auf der 32-Bit-Plattform besteht eine VMT bei positiven Offsets aus einer Liste mit 32-Bit-Methodenzeigern (64-Bit-Methodenzeiger auf der 64-Bit-Plattform). Für jede benutzerdefinierte virtuelle Methode des Klassentyps ist ein Zeiger vorhanden. Die Zeiger sind in der Reihenfolge der Deklaration angeordnet. Jeder Eintrag enthält die Adresse des Eintrittspunkts der entsprechenden virtuellen Methode. Dieses Layout ist zur V-Tabelle von C++ und zu COM kompatibel. Bei negativen Offsets enthält eine VMT die Anzahl der Felder, die in Delphi intern implementiert sind. In einer Anwendung sollten diese Informationen mit den Methoden von TObject abgerufen werden, da sich dieses Layout bei künftigen Implementierungen von Delphi ändern kann.

Struktur der virtuellen Methodentabelle

Offset
Win32, OSX
Offset
Win64
Offset
iOS/ARM, Android/ARM
Offset
iOS/Simulator
Typ Beschreibung Konstante in System.pas

-88

-200

-108

-96

Zeiger

Zeiger auf die virtuelle Methodentabelle (oder nil)

vmtSelfPtr

-84

-192

-104

-92

Zeiger

Zeiger auf die Interface-Tabelle (oder nil)

vmtIntfTable

-80

-184

-100

-88

Zeiger

Zeiger auf die Informationstabelle zur Automatisierung (oder nil)

vmtAutoTable

-76

-176

-96

-84

Zeiger

Zeiger auf die Instanzeninitialisierungstabelle (oder nil)

vmtInitTable

-72

-168

-92

-80

Zeiger

Zeiger auf die Typinformationstabelle (oder nil)

vmtTypeInfo

-68

-160

-88

-76

Zeiger

Zeiger auf die Felddefinitionstabelle (oder nil)

vmtFieldTable

-64

-152

-84

-72

Zeiger

Zeiger auf die Methodendefinitionstabelle (oder nil)

vmtMethodTable

-60

-144

-80

-68

Zeiger

Zeiger auf die dynamische Methodentabelle (oder nil)

vmtDynamicTable

-56

-136

-76

-64

Zeiger

Zeiger auf einen kurzen String, der den Klassennamen enthält

vmtClassName

-52

-128

-72

-60

Cardinal

Instanzgröße in Byte

vmtInstanceSize

-48

-120

-68

-56

Zeiger

Zeiger auf einen Zeiger auf die Vorfahrklasse (oder nil)

vmtParent

n/v

n/v

-64

-52

Zeiger

Eintrittspunkt der Methode __ObjAddRef

vmtObjAddRef

n/v

n/v

-60

-48

Zeiger

Eintrittspunkt der Methode __ObjRelease

vmtObjRelease

-44

-112

-56

-44

Zeiger

Eintrittspunkt der Methode Equals

vmtEquals

-40

-104

-52

-40

Zeiger

Eintrittspunkt der Methode GetHashCode

vmtGetHashCode

-36

-96

-48

-36

Zeiger

Eintrittspunkt der Methode ToString

vmtToString

-32

-88

-44

-32

Zeiger

Zeiger auf den Eintrittspunkt der Methode SafecallException (oder nil)

vmtSafeCallException

-28

-80

-40

-28

Zeiger

Eintrittspunkt der Methode AfterConstruction

vmtAfterConstruction

-24

-72

-36

-24

Zeiger

Eintrittspunkt der Methode BeforeDestruction

vmtBeforeDestruction

-20

-64

-32

-20

Zeiger

Eintrittspunkt der Methode Dispatch

vmtDispatch

-16

-56

-28

-16

Zeiger

Eintrittspunkt der Methode DefaultHandler

vmtDefaultHandler

-12

-48

-24

-12

Zeiger

Eintrittspunkt der Methode NewInstance

vmtNewInstance

-8

-40

-20

-8

Zeiger

Eintrittspunkt der Methode FreeInstance

vmtFreeInstance

-4

-32

-16

-4

Zeiger

Eintrittspunkt des Destruktors Destroy

vmtDestroy

0

0

0

0

Zeiger

Eintrittspunkt der ersten benutzerdefinierten virtuellen Methode

4

8

4

4

Zeiger

Eintrittspunkt der zweiten benutzerdefinierten virtuellen Methode

Klassenreferenztypen

Auf der 32-Bit-Plattform (Win32, OSX, iOS und Android) wird der Wert einer Klassenreferenz als 32-Bit-Zeiger auf die virtuelle Methodentabelle (VMT) einer Klasse gespeichert.

Auf der 64-Bit-Plattform (Win64) wird der Wert einer Klassenreferenz als 64-Bit-Zeiger auf die virtuelle Methodentabelle (VMT) einer Klasse gespeichert.

Variante Typen

Varianten sind beim Implementieren von variantenbezogenen RTL-Funktionen vom Boxing und Unboxing von Daten in einen Objekt-Wrapper sowie von Delphi-Hilfsklassen abhängig.

Auf der 32-Bit-Plattform wird eine Variante als 16-Byte-Record gespeichert, der einen Typcode und einen Wert (oder eine Referenz auf einen Wert) des durch den Code bezeichneten Typs enthält. Auf der 64-Bit-Plattform wird eine Variante als 24-Byte-Record gespeichert. Die Units System und System.Variants definieren Konstanten und Typen für Varianten.

Der Typ TVarData steht für die interne Struktur einer Variante (in Windows entspricht er dem Typ Variant, der von COM und der Win32-API verwendet wird). Mit dem Typ TVarData kann bei einer Typumwandlung von Varianten auf die interne Struktur einer Variable zugegriffen werden. Der Record TVarData enthält die folgenden Felder:

  • Das Feld VType mit dem Typ TVarType hat die Größe Word (16 Bit). VType enthält den Typcode der Variante in den niederwertigen 12 Bit (die Bits, die von der Konstante varTypeMask = $FFF definiert werden). Zusätzlich zeigt das Bit varArray = $2000 an, ob es sich bei der Variante um ein Array handelt. Das Bit varByRef (= $4000) gibt an, ob die Variante eine Referenz oder einen Wert enthält.
  • Die Felder Reserved1, Reserved2 und Reserved3 (Word-Größe) werden nicht verwendet.

Der Inhalt der restlichen 8 Byte (32-Bit-Plattform) oder 16 Byte (64-Bit-Plattform) eines TVarData-Records hängt folgendermaßen vom Feld VType ab:

  • Wenn weder das Bit varArray noch das Bit varByRef gesetzt ist, enthält die Variante einen Wert des gegebenen Typs.
  • Wenn das Bit varArray gesetzt ist, enthält die Variante einen Zeiger auf eine TVarArray-Struktur, die das Array definiert. Der Typ jedes Array-Elements ist durch die varTypeMask-Bits des Feldes VType festgelegt.
  • Wird das Bit varByRef gesetzt, enthält die Variante eine Referenz auf einen Wert des Typs, der durch die Bits varTypeMask und varArray im Feld VType definiert ist.

Der Typcode varString ist "private". Varianten, die einen varString-Wert enthalten, sollten nicht an Nicht-Delphi-Funktionen übergeben werden. Auf der Windows-Plattform sorgt die Automatisierungsunterstützung von Delphi dafür, dass varString-Varianten vor der Übergabe an externe Funktionen automatisch in varOleStr-Varianten umgewandelt werden.

Siehe auch