Formats de données internes (Delphi)
Les rubriques suivantes décrivent les formats internes des types de données Delphi.
Types entiers
Les valeurs entières ont la représentation interne suivante dans Delphi.
Types entiers non signés indépendants de la plate-forme
Les valeurs des types entiers indépendants de la plate-forme occupent le même nombre de bits sur toutes les plates-formes.
Les valeurs des types entiers non signés sont toujours positives et n'impliquent pas un bit de signe comme le font les types entiers signés. Tous les bits des types entiers non signés sont occupés par la magnitude de la valeur et n'ont aucune autre signification.
Byte, UInt8
Byte et UInt8 sont des nombres entiers positifs non signés sur 1 octet (8 bits). La magnitude occupe la totalité des 8 bits.
Word et UInt16
Word et UInt16 sont des nombres entiers non signés sur 2 octets (16 bits).
Cardinal, LongWord et UInt32
Cardinal, LongWord et UInt32 sont des nombres entiers non signés sur 4 octets (32 bits).
UInt64
UInt64 sont des nombres entiers non signés sur 8 octets (64 bits).
Types entiers signés indépendants de la plate-forme
Les valeurs des types entiers signés n'ont pas besoin de représenter le signe d'un nombre par un bit de signe de tête, exprimé par le bit le plus significatif. Le signe de bit est 0 pour un nombre positif, et 1 pour un nombre négatif. Les autres bits d'un nombre entier signé positif sont occupés par la magnitude. Dans un nombre entier signé négatif, les autres bits sont occupés par la représentation en complément à 2 de la magnitude de la valeur (valeur absolue).
Pour transformer le complément à 2 en une magnitude :
- En partant de la droite, trouvez le premier '1'.
- Inversez tous les bits situés sur la gauche de celui-ci.
Par exemple :
| Exemple 1 | Exemple 2 | |
|---|---|---|
| Magnitude | 0101010
|
1010101
|
|
Complément à 2 |
1010110
|
01010110
|
ShortInt, Int8
Shortint et Int8 sont des nombres entiers signés sur 1 octet (8 bits). Le bit de signe occupe le bit 7 le plus significatif, la magnitude ou le complément à 2 occupe les autres 7 bits.
SmallInt et Int16
SmallInt et Int16 sont des nombres entiers signés sur 2 octets (16 bits).
Integer, LongInt et Int32
Integer, LongInt et Int32 sont des nombres entiers signés sur 4 octets (32 bits).
Int64
Int64 sont des nombres entiers signés sur 8 octets (64 bits).
Types entiers dépendants de la plate-forme
Les types entiers dépendants de la plate-forme sont NativeUInt et NativeInt. Les types entiers dépendants de la plate-forme sont transformés pour s'ajuster à la taille en bits de la plate-forme cible en cours. Sur les plates-formes 64 bits, ils occupent 64 bits, sur les plates-formes 32 bits, ils occupent 32 bits. Quand la taille de la plate-forme cible est la même que la plate-forme CPU, un nombre entier dépendant de la plate-forme correspond alors exactement à la taille des registres CPU. Ces types sont souvent utilisés quand de meilleures performances sont souhaitées pour un type de CPU et système d'exploitation particuliers.
Entier non signé NativeUInt
NativeUInt est le type entier non signé dépendant de la plate-forme. La taille et la représentation interne de NativeUInt dépend de la plate-forme en cours. Sur les plates-formes 32 bits, NativeUInt est équivalent au type Cardinal. Sur les plates-formes 64 bits, NativeUInt est équivalent au type UInt64.
Entier signé NativeInt
NativeInt est le type entier signé dépendant de la plate-forme. La taille et la représentation interne de NativeInt dépend de la plate-forme en cours. Sur les plates-formes 32 bits, NativeInt est équivalent au type Integer. Sur les plates-formes 64 bits, NativeInt est équivalent au type Int64.
Types sous-intervalle entiers
Quand vous utilisez des constantes entières pour définir les bornes inférieure et supérieure d'un type sous-intervalle, vous définissez un type sous-intervalle entier. Un type sous-intervalle entier représente un sous-ensemble des valeurs d'un type entier (appelé le type de base). Le type de base est le type entier le plus petit qui contient l'intervalle spécifié (contient à la fois les bornes inférieure et supérieure).
Le format de données interne d'une variable de type sous-intervalle entier dépend de ses bornes inférieure et supérieure :
- Si les deux bornes sont contenues dans l'intervalle -128..127 (Shortint), la variable est stockée sous la forme d'un octet signé.
- Si les deux bornes sont contenues dans l'intervalle 0..255 (Byte), la variable est stockée sous la forme d'un octet non signé.
- Si les deux bornes sont contenues dans l'intervalle -32768..32767 (Smallint), la variable est stockée sous la forme d'un mot signé.
- Si les deux bornes sont contenues dans l'intervalle 0..65535 (Word), la variable est stockée sous la forme d'un mot non signé.
- Si les deux bornes sont contenues dans l'intervalle -2147483648..2147483647 (Longint), la variable est stockée sous la forme d'un double mot signé.
- Si les deux bornes sont contenues dans l'intervalle 0..4294967295 (Longword), la variable est stockée sous la forme d'un double mot non signé.
- Sinon, la variable est stockée sous la forme d'un quadruple mot signé (Int64).
Remarque : Un "mot" occupe deux octets.
Types caractères
Sur la plate-forme Win32 :
- Char et WideChar sont stockés sous la forme d'une variable d'un mot non signé, en utilisant normalement l'encodage UTF-16 ou Unicode.
- AnsiChar et les rangées d'un type Char sont stockés sous la forme d'un octet non signé. Dans Delphi 2007 et les versions antérieures, Char était représenté sous la forme d'un AnsiChar. Le type de caractère utilisé avec les chaînes courtes est toujours AnsiChar et il est stocké dans des valeurs d'octets non signés.
- Le type de chaîne longue par défaut (string) est à présent UnicodeString, avec compteur de références similaire à AnsiString, l'ancien type de chaîne longue par défaut. La compatibilité avec l'ancien code peut nécessiter l'usage du type AnsiString.
- WideString est composé de WideChars comme UnicodeString, mais n'utilise pas le comptage des références.
Types booléens
Un type Boolean est stocké sous la forme d'un Byte, un type ByteBool est stocké sous la forme d'un Byte, un type WordBool est stocké sous la forme d'un Word et un LongBool est stocké sous la forme d'un Longint.
Un Boolean peut prendre les valeurs 0 (False) et 1 (True). Les types ByteBool, WordBool et LongBool acceptent les valeurs nulles (False) et non nulles (True).
Types énumérés
Un type énuméré est stocké sous la forme d'un octet non signé si l'énumération ne comporte pas plus de 256 valeurs et si le type a été déclaré dans l'état {$Z1} (ce qui est le cas par défaut). Si un type énuméré a plus de 256 valeurs ou si le type a été déclaré en mode {$Z2}, il est stocké sous la forme d'un mot non signé. Si un type énuméré est déclaré dans l'état {$Z4}, il est stocké sous la forme d'un mot double non signé.
Types réels
Les types réels stockent la représentation binaire d'un signe, (+ ou -), d'un exposant et d'une mantisse (significand). Une valeur réelle a la forme :
+/- significand * 2^exponent
où significand ne possède qu'un bit à gauche de la virgule décimale binaire (c'est-à-dire, 0 <= significand < 2).
Dans les images suivantes, le bit le plus significatif est toujours à gauche et le bit le moins significatif à droite. Les nombres en haut indiquent la largeur (exprimée en bits) de chaque champ, les éléments les plus à gauche étant stockés aux adresses les plus élevées. Par exemple, pour une valeur Real48, e est stocké dans le premier octet, f dans les cinq octets suivants et s dans le bit le plus significatif du dernier octet.
Le type Real48
Sur les plates-formes Win32, un nombre Real48 sur 6 octets (48 bits) est divisé en trois champs.
|
1 |
39 |
8 |
|
|
|
|
Si 0 < e <= 255, la valeur v du nombre est donnée par :
v = (-1)s * 2(e-129) * (1.f)
Si e = 0, alors v = 0.
Le type Real48 ne peut pas stocker des nombres dénormalisés, NaN ou infinis. Les nombres dénormalisés deviennent nuls quand ils sont stockés dans un Real48, et les nombres NaN et infinis produisent une erreur de dépassement de capacité lorsqu'on tente de les stocker dans un Real48.
Le type Single
Un nombre Single sur 4 octets (32 bits) est divisé en trois champs.
|
1 |
8 |
23 |
|
|
|
|
La valeur v du nombre est donnée par :
- Si
0 < e < 255, alorsv = (-1)s * 2(e-127) * (1.f)
- Si
e = 0etf <> 0, alorsv = (-1)s * 2(-126) * (0.f)
- Si
e = 0etf = 0, alorsv = (-1)s * 0
- Si
e = 255etf = 0, alorsv = (-1)s * Inf
- Si
e = 255etf <> 0, alorsvest un NaN
Le type Double
Dans l'implémentation en cours, le type Real est équivalent à Double.
Un nombre Double sur 8 octets (64 bits) est divisé en trois champs.
|
1 |
11 |
52 |
|
|
|
|
La valeur v du nombre est donnée par :
- Si
0 < e < 2047, alorsv = (-1)s * 2(e-1023) * (1.f)
- Si
e = 0etf <> 0, alorsv = (-1)s * 2(-1022) * (0.f)
- Si
e = 0etf = 0, alorsv = (-1)s * 0
- Si
e = 2047etf = 0, alorsv = (-1)s * Inf
- Si
e = 2047etf <> 0, alorsvest un NaN
Le type Extended
Sur les plates-formes 64 bits, le type Extended est un alias pour Double, qui est seulement de 8 octets. Voir la section Le type Double ci-dessus. Pour de plus amples informations, voir Considérations Delphi pour les applications multiplates-formes.
Sur les plates-formes 32 bits, un nombre Extended est représenté sur 10 octets (80 bits). Un nombre Extended est divisé en quatre champs.
|
1 |
15 |
1 |
63 |
|
|
|
|
|
La valeur v du nombre est donnée par :
- Si
0 <= e < 32767, alorsv = (-1)s * 2(e-16383) * (i.f)
- Si
e = 32767etf = 0, alorsv = (-1)s * Inf
- Si
e = 32767etf <> 0, alorsvest un NaN
Le type Comp
Un nombre Comp sur 8 octets (64 bits) est stocké comme un entier signé sur 64 bits.
Le type Currency
Un nombre Currency sur 8 octets (64 bits) est stocké sous la forme d'un entier scalaire et signé sur 64 bits, dont les 4 chiffres les moins significatifs représentent implicitement 4 positions décimales.
Types pointeur
Sur les plates-formes 32 bits, un type pointeur est stocké dans 4 octets sous une adresse 32 bits. Sur les plates-formes 64 bits, un type pointeur est stocké dans 8 octets sous une adresse 64 bits.
La valeur pointeur nil est stockée sous la forme zéro.
Types chaînes courtes
Une chaîne occupe le nombre d'octets correspondant à sa longueur maximale plus un. Le premier octet contient la longueur dynamique réelle de la chaîne et les octets suivants contiennent ses caractères.
L'octet de longueur et les caractères sont des valeurs non signées. La longueur de chaîne maximale est de 255 caractères plus un octet de longueur (string[255]).
Types chaînes longues
Une variable chaîne de type UnicodeString ou AnsiString occupe 4 octets de mémoire, qui contiennent un pointeur sur une chaîne allouée dynamiquement. Quand une variable chaîne est vide (contient une chaîne d'une longueur de zéro), le pointeur chaîne est nil et aucune mémoire dynamique n'est associée avec la variable chaîne. Pour une valeur de chaîne non vide, le pointeur chaîne pointe sur un bloc de mémoire alloué dynamiquement qui contient la valeur de la chaîne en plus des informations décrivant la chaîne. Le tableau suivant montre le contenu d'un bloc mémoire de chaîne longue.
Disposition mémoire dynamique d'une chaîne (Win32 seulement)
| Décalage | Contenu |
|---|---|
|
-12 |
Page de code 16 bits de données chaîne |
|
-10 |
Taille d'élément 16 bits de données chaîne |
|
-8 |
Compteur de références sur 32 bits |
|
-4 |
Longueur en octets |
|
0..Longueur - 1 |
Chaîne de caractères de données dimensionnées en éléments |
|
Longueur*Taille d'élément |
Caractère NULL |
Le caractère NULL à la fin d'un bloc mémoire de chaîne est automatiquement géré par le compilateur et les routines de gestion de chaîne intégrées. Cela rend possible le transtypage direct d'une chaîne par une chaîne à zéro terminal.
Pour les littéraux chaîne, le compilateur génère un bloc mémoire de même disposition qu'une chaîne allouée dynamiquement mais avec un compte de références de -1. Les constantes chaîne sont traitées de la même façon, la seule différence étant que les littéraux sont un pointeur sur un bloc de compteur de références de -1.
Quand un pointeur sur une structure chaîne (source) est assigné à une variable chaîne (destination), le compteur de références dicte la façon de faire. Généralement, le compte de références est diminué pour la destination et augmenté pour la source parce que les deux pointeurs, source et destination, pointeront sur le même bloc de mémoire après l'affectation.
Si le compte de références source est -1 (constante chaîne), une nouvelle structure est créée avec un compte de références de 1. Si la destination ne vaut pas nil, le compteur de références est diminué. S'il atteint 0, la structure est désallouée de la mémoire. Si la destination vaut nil, aucune action supplémentaire n'est entreprise. La destination pointera alors sur la nouvelle structure.
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.
Si le compte de références source n'est pas -1, il est incrémenté et la destination pointera sur lui.
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.
Remarque : Aucune variable chaîne ne peut pointer sur une structure avec un compte de références de 0. Les structures sont toujours désallouées lorsqu'elles atteignent un compte de références de 0 et ne peuvent pas être modifiées lorsqu'elles ont un compte de références de -1.
Types chaînes étendues
Sur Win32, une variable chaîne étendue occupe 4 octets de mémoire qui contiennent un pointeur sur une chaîne allouée dynamiquement. Lorsqu'une variable chaîne étendue est vide (contient une chaîne d'une longueur de zéro), le pointeur de chaîne est nil et aucune mémoire dynamique n'est associée avec la variable chaîne. Pour une valeur de chaîne non vide, le pointeur de chaîne pointe un bloc de mémoire alloué dynamiquement qui contient la valeur de la chaîne en plus d'un indicateur de longueur 32 bits. Le tableau suivant montre le contenu d'un bloc mémoire de chaîne étendue sur Windows.
Disposition en mémoire dynamique d'une chaîne étendue (Win32 seulement)
| Offset | Contenu |
|---|---|
|
-4 |
Indicateur de longueur sur 32 bits (en octets) |
|
0..Longueur -1 |
Chaîne de caractères |
|
Longueur |
Caractère NULL |
La longueur de la chaîne est évaluée en octets, c'est donc le double du nombre de caractères contenus dans la chaîne.
Le caractère NULL à la fin d'un bloc mémoire de chaîne étendue est automatiquement géré par le compilateur et les routines de gestion de chaîne intégrées. Cela rend possible le transtypage direct d'une chaîne étendue par une chaîne à zéro terminal.
Types ensemble
Un ensemble est un tableau de bits dans lequel chaque bit indique si un élément est présent dans l'ensemble ou non. Le nombre maximal d'éléments dans un ensemble est 256, et un ensemble n'occupe donc jamais plus de 32 octets. Le nombre d'octets occupés par un ensemble particulier est calculé comme suit :
(Max div 8) - (Min div 8) + 1
où Max et Min sont les bornes inférieure et supérieure du type de base de l'ensemble. Le numéro de l'octet d'un élément particulier E est :
(E div 8) - (Min div 8)
Et le numéro du bit à l'intérieur de cet octet est :
E mod 8
où E désigne la valeur ordinale de l'élément. Quand c'est possible, le compilateur stocke les ensembles dans les registres CPU, mais un ensemble réside toujours en mémoire s'il est plus grand que le type entier générique ou si le programme contient du code qui utilise l'adresse de l'ensemble.
Types tableaux statiques
Sur la plate-forme Win32, un tableau statique est stocké sous la forme d'une suite contiguë de variables du type de composant du tableau. Les éléments d'index les plus faibles sont stockés aux adresses mémoire les plus faibles. Dans un tableau multidimensionnel, la dimension la plus à droite est celle qui s'incrémente en premier.
Types tableaux dynamiques
Sur la plate-forme Win32, une variable tableau dynamique occupe 4 octets de mémoire qui contiennent un pointeur sur le tableau alloué dynamiquement. Quand la variable est vide (non initialisée) ou contient un tableau de longueur nulle, le pointeur vaut nil et il n'y a pas de mémoire dynamique associée à la variable. Pour un tableau non vide, la variable pointe sur un bloc de mémoire alloué dynamiquement qui contient en plus du tableau un indicateur de longueur sur 32 bits et un compteur de références sur 32 bits. Le tableau suivant indique l'organisation du bloc de mémoire de tableau alloué dynamiquement.
Disposition en mémoire d'un tableau dynamique (Win32 seulement)
| Offset | Contenu |
|---|---|
|
-8 |
Compteur de références sur 32 bits |
|
-4 |
Indicateur de longueur sur 32 bits (nombre d'éléments) |
|
0..Longueur * (taille d'élément) -1 |
Eléments du tableau |
Types enregistrements
Quand un type enregistrement est déclaré dans l'état {$A+} (valeur par défaut) et que la déclaration ne comprend pas de modificateur packed, le type est unpacked record, et les champs de l'enregistrement sont alignés pour faciliter l'accès par la CPU, et conformément à la plate-forme. L'alignement est contrôlé par le type de chaque champ. Chaque type de données a un alignement inhérent, automatiquement calculé par le compilateur. L'alignement peut être 1, 2, 4 ou 8 et représente la limite en octets sur laquelle une valeur du type doit être stockée pour que l'accès soit plus efficace. Le tableau suivant liste les alignements pour tous les types de données.
Masques d'alignement des types (Win32 seulement)
| Type | Alignement |
|---|---|
|
Types ordinaux |
Taille du type (1, 2, 4 ou 8) |
|
Types réels |
2 pour Real48, 4 pour Single, 8 pour Double et Extended |
|
Types chaînes courtes |
1 |
|
Types tableaux |
Identique au type des éléments du tableau |
|
Types enregistrements |
Le plus grand alignement des champs de l'enregistrement |
|
Types ensembles |
Taille du type si 1, 2 ou 4, sinon 1 |
|
Tous les autres types |
Déterminé par la directive $A |
Pour que l'alignement des champs soit correct dans un type enregistrement décompacté, le compilateur insère un octet inutilisé avant les champs ayant un alignement de 2, et jusqu'à 3 octets inutilisés avant les champs ayant un alignement de 4, si nécessaire. Enfin, le compilateur arrondit la taille totale de l'enregistrement jusqu'à la limite en octets spécifiée par l'alignement le plus grand des champs.
Alignement packed implicite des champs avec une spécification de type commune
Les versions antérieures du compilateur Delphi, telles que Delphi 7 et avant, appliquaient implicitement l'alignement packed aux champs qui étaient déclarés ensemble, c'est-à-dire les champs ayant une spécification de type commune. Les compilateurs plus récents peuvent reproduire ce comportement si vous spécifiez la directive {$OLDTYPELAYOUT ON}. Cette directive aligne sur les octets (alignement packed) les champs ayant une spécification de type commune, même si la déclaration n'inclut pas le modificateur packed et si le type enregistrement n'est pas déclaré dans l'état {$A-}.
Ainsi, par exemple, soit la déclaration suivante :
{$OLDTYPELAYOUT ON} type TMyRecord = record A, B: Extended; C: Extended; end; {$OLDTYPELAYOUT OFF}
A et B sont alignés sur des limites en octets (alignement packed) car la directive {$OLDTYPELAYOUT ON} est spécifiée et que A et B partagent la même spécification de type. Toutefois, pour le champ C déclaré séparément, le compilateur utilise le comportement par défaut et complète la structure avec des octets inutilisés pour garantir que le champ apparaît sur une limite de mot quadruple.
Quand un type enregistrement est déclaré dans l'état {$A-} ou que la déclaration inclut le modificateur packed, les champs de l'enregistrement ne sont pas alignés, mais des décalages consécutifs leur sont plutôt assignés. La taille totale d'un tel enregistrement packed est simplement la taille de tous les champs. Puisque l'alignement des données peut changer, c'est une bonne idée de compacter toute structure d'enregistrement que vous avez l'intention d'écrire sur disque ou de passer en mémoire à un autre module compilé avec une version du compilateur différente.
Types de fichiers
Sur les plates-formes Win32, les types de fichiers sont représentés sous forme d'enregistrements. Les fichiers typés et non typés occupent 592 octets, disposés comme suit :
type TFileRec = packed record Handle: Integer; Mode: word; Flags: word; case Byte of 0: (RecSize: Cardinal); 1: (BufSize: Cardinal; BufPos: Cardinal; BufEnd: Cardinal; BufPtr: PChar; OpenFunc: Pointer; InOutFunc: Pointer; FlushFunc: Pointer; CloseFunc: Pointer; UserData: array[1..32] of Byte; Name: array[0..259] of Char; ); end;
Les fichiers texte occupent 848 octets, disposés comme suit :
type TTextBuf = array[0..127] of Char; TTextRec = packed record Handle: Integer; Mode: word; Flags: word; BufSize: Cardinal; BufPos: Cardinal; BufEnd: Cardinal; BufPtr: PChar; OpenFunc: Pointer; InOutFunc: Pointer; FlushFunc: Pointer; CloseFunc: Pointer; UserData: array[1..32] of Byte; Name: array[0..259] of Char; Buffer: TTextBuf; end;
Handle contient le handle du fichier (quand le fichier est ouvert).
Le champ Mode peut prendre l'une des valeurs suivantes :
const fmClosed = $D7B0; fmInput= $D7B1; fmOutput = $D7B2; fmInOut= $D7B3;
où fmClosed indique que le fichier est fermé. fmInput et fmOutput indiquent un fichier texte qui a été réinitialisé (fmInput) ou réécrit (fmOutput), fmInOut indique un fichier typé ou non typé qui a été réinitialisé ou réécrit. Toute autre valeur indique que la variable fichier n'a pas été affectée (et qu'elle n'est donc pas initialisée).
Le champ UserData est disponible pour les routines écrites par l'utilisateur afin d'y stocker des données.
Name contient le nom du fichier, qui est une séquence de caractères terminée par un caractère null (#0).
Pour les fichiers typés et les fichiers non typés, RecSize contient la longueur de l'enregistrement en octets, et le champ Private est inutilisé mais réservé.
Pour les fichiers texte, BufPtr est un pointeur sur un tampon de BufSize octets, BufPos est l'index dans le tampon du prochain caractère à lire à ou écrire, et BufEnd représente le nombre de caractères valides dans le tampon. OpenFunc, InOutFunc, FlushFunc et CloseFunc sont des pointeurs sur les routines d'E/S qui contrôlent le fichier ; voir Fonctions de périphérique. Flags détermine le style de saut de ligne comme suit.
|
bit 0 vide |
Sauts de ligne LF |
|
bit 0 défini |
Sauts de ligne CRLF |
Tous les autres bits Flags sont réservés pour un usage ultérieur.
Remarque : Pour l'utilisation du type UnicodeString (le type de chaîne Delphi par défaut), les divers types de flux de l'unité Classes (TFileStream, TStreamReader, TStreamWriter, et ainsi de suite) sont plus utiles, puisque les anciens types de fichiers ont une fonctionnalité Unicode limitée, particulièrement l'ancien type de fichier texte.
Types procéduraux
Sur la plate-forme Win32, un pointeur de procédure est stocké sous la forme d'un pointeur 32 bits sur le point d'entrée d'une procédure ou d'une fonction. Un pointeur de méthode est stocké sous la forme d'un pointeur 32 bits sur le point d'entrée d'une méthode, suivi par un pointeur 32 bits sur un objet.
Types classe
Sur la plate-forme Win32, une valeur d'un type classe est stockée sous la forme d'un pointeur 32 bits sur une instance de la classe (ce qu'on appelle aussi un objet). Le format interne d'un objet est similaire à celui d'un enregistrement. Les champs d'un objet sont stockés sous la forme d'une suite contiguë de variables, dans l'ordre de leur déclaration. Les champs sont toujours alignés, correspondant à un type enregistrement décompacté. Les champs hérités d'une classe ancêtre sont stockés avant les nouveaux champs définis dans la classe descendante.
Les 4 premiers octets de chaque objet contiennent un pointeur sur la table des méthodes virtuelles (VMT) de la classe. Il n'existe qu'une seule VMT par classe (et non une par objet), mais deux types de classes distincts ne partagent jamais une VMT, quel que soit leur degré de similitude. Les VMTs sont construites automatiquement par le compilateur et ne sont jamais manipulées directement par un programme. Les pointeurs sur les VMTs, qui sont automatiquement stockés par les méthodes constructeur dans les objets qu'elles créent, ne sont jamais directement manipulés par un programme.
Le contenu d'une VMT est présenté dans le tableau ci-dessous. Aux décalages positifs, une VMT se compose d'une liste de pointeurs de méthodes sur 32 bits, un par méthode virtuelle définie par l'utilisateur dans le type classe, dans l'ordre de la déclaration. Chaque emplacement contient l'adresse du point d'entrée de la méthode virtuelle correspondante. Cette structure est compatible avec une v-table C++ ainsi qu'avec COM. Aux décalages négatifs, une VMT contient plusieurs champs internes à Delphi. Les applications doivent utiliser les méthodes définies dans TObject pour obtenir ces informations, car cette structure pourra être amenée à changer dans les versions futures du langage Delphi.
Disposition des tables de méthodes virtuelles (Win32 seulement)
| Offset | Type | Description |
|---|---|---|
|
-76 |
Pointeur |
Pointeur sur la table des méthodes virtuelles (ou nil) |
|
-72 |
Pointeur |
Pointeur sur la table d'interface (ou nil) |
|
-68 |
Pointeur |
Pointeur sur la table des informations d'automation (ou nil) |
|
-64 |
Pointeur |
Pointeur sur la table d'initialisation d'instance (ou nil) |
|
-60 |
Pointeur |
Pointeur sur la table des informations de type (ou nil) |
|
-56 |
Pointeur |
Pointeur sur la table de définition des champs (ou nil) |
|
-52 |
Pointeur |
Pointeur sur la table de définition des méthodes (ou nil) |
|
-48 |
Pointeur |
Pointeur sur la table des méthodes dynamiques (ou nil) |
|
-44 |
Pointeur |
Pointeur sur une chaîne courte contenant le nom de la classe |
|
-40 |
Cardinal |
Taille de l'instance en octets |
|
-36 |
Pointeur |
Pointeur sur un pointeur vers la classe ancêtre (ou nil) |
|
-32 |
Pointeur |
Pointeur sur le point d'entrée de la méthode SafecallException (ou nil) |
|
-28 |
Pointeur |
Point d'entrée de la méthode AfterConstruction |
|
-24 |
Pointeur |
Point d'entrée de la méthode BeforeDestruction |
|
-20 |
Pointeur |
Point d'entrée de la méthode Dispatch |
|
-16 |
Pointeur |
Point d'entrée de la méthode DefaultHandler |
|
-12 |
Pointeur |
Point d'entrée de la méthode NewInstance |
|
-8 |
Pointeur |
Point d'entrée de la méthode FreeInstance |
|
-4 |
Pointeur |
Point d'entrée du destructeur Destroy |
|
0 |
Pointeur |
Point d'entrée de la première méthode virtuelle définie par l'utilisateur |
|
4 |
Pointeur |
Point d'entrée de la seconde méthode virtuelle définie par l'utilisateur |
Types références de classe
Sur la plate-forme Win32, une valeur référence de classe est stockée sous la forme d'un pointeur 32 bits sur la table des méthodes virtuelles (VMT) d'une classe.
Types variants
La discussion suivante relative aux types variants de la disposition interne s'applique uniquement à la plate-forme Win32. Les variants invoquent la conversion boxing et unboxing des données dans une enveloppe d'objet, ainsi que les classes utilitaires de Delphi pour implémenter les fonctions RTL associées aux variants.
Sur la plate-forme Win32, un variant est stocké sous la forme d'un enregistrement de 16 octets contenant un code de type et une valeur (ou une référence à une valeur), selon le type donné par le code. Les unités System et Variants définissent des constantes et des types pour les variants.
Le type TVarData représente la structure interne d'une variable Variant (sur Windows, il est identique au type Variant utilisé par COM et l'API Win32). Le type TVarData peut être utilisé dans des transtypages de variables Variant pour accéder à la structure interne d'une variable. L'enregistrement TVarData contient les champs suivants :
-
VTypecontient le code du type du variant dans ses 12 bits de poids faible (les bits définis par la constante varTypeMask). De plus, le bit varArray peut être défini pour indiquer que le variant est un tableau, et le bit varByRef peut être défini pour indiquer que le variant contient une référence (et non une valeur). - Les champs Reserved1, Reserved2 et Reserved3 ne sont pas utilisés.
Le contenu des 8 octets restants d'un enregistrement TVarData dépend du champ VType comme suit :
- Si aucun des bits varArray ou varByRef ne sont définis, le variant contient une valeur du type donné.
- Si le bit varArray est défini, le variant contient un pointeur sur une structure
TVarArraydéfinissant un tableau. Le type de chaque élément du tableau est donné par les bits varTypeMask du champVType. - Si le bit varByRef est défini, le variant contient une référence à une valeur du type donné par les bits varTypeMask et varArray dans le champ
VType.
Le code du type varString est privé. Les variants contenant une valeur varString ne devraient jamais être passés à une fonction non-Delphi. Sur Win32, le support Automation de Delphi convertit automatiquement les variants varString en variants varOleStr avant de les transmettre sous forme de paramètres aux fonctions externes.











