Formats de données internes (Delphi)

De RAD Studio
Aller à : navigation, rechercher

Remonter à Gestion de la mémoire - Index


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.

Entier non signé sur 8 bits

Word et UInt16

Word et UInt16 sont des nombres entiers non signés sur 2 octets (16 bits).

Entier non signé sur 16 bits

FixedUInt, Cardinal et UInt32

FixedUInt, Cardinal et UInt32 sont des nombres entiers non signés sur 4 octets (32 bits).

Entier non signé sur 32 bits

UInt64

UInt64 sont des nombres entiers non signés sur 8 octets (64 bits).

Entier non signé sur 64 bits

Types entiers signés indépendants de la plate-forme

Les valeurs des types entiers signés représentent 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 :

  1. En partant de la droite, trouvez le premier '1'.
  2. 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 0101011

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.

Entier signé positif sur 8 bits
Entier signé négatif sur 8 bits

SmallInt et Int16

SmallInt et Int16 sont des nombres entiers signés sur 2 octets (16 bits).

Entier signé positif sur 16 bits
Entier signé négatif sur 16 bits

FixedInt, Integer et Int32

FixedInt, Integer et Int32 sont des nombres entiers signés sur 4 octets (32 bits).

Entier signé positif sur 32 bits
Entier signé négatif sur 32 bits

Int64

Int64 sont des nombres entiers signés sur 8 octets (64 bits).

Entier signé positif sur 64 bits
Entier signé négatif sur 64 bits

Types entiers dépendants de la plate-forme

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 (à l'exception des types LongInt et LongWord). 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épendent 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épendent 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.

LongInt et LongWord

LongInt définit le type entier signé et LongWord définit le type entier non signé. La taille des types entiers LongInt et LongWord dépendant de la plate-forme est modifiée selon la plate-forme, à l'exception de Windows 64 bits où la taille reste inchangée (32 bits).

Taille
Plates-formes Windows 32 bits et 64 bits Plates-formes iOS 64 bits
LongInt 32 bits (4 octets) 64 bits (8 octets)

LongWord

32 bits (4 octets) 64 bits (8 octets)
Remarque: Les plates-formes 32 bits dans RAD Studio incluent Windows 32 bits, macOS 32 bits, iOS 32 bits et Android.

Sur les plates-formes iOS 64 bits, si vous voulez utiliser :

Types sous-intervalle entiers

Lorsque 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 (FixedInt et LongInt sur les plates-formes Windows 32 bits et 64 bits), la variable est stockée sous la forme d'un double mot signé.
  • Si les deux bornes sont contenues dans l'intervalle 0..4294967295 (FixedUInt et LongWord sur les plates-formes Windows 32 bits et 64 bits), la variable est stockée sous la forme d'un double mot non signé.
  • Si les deux bornes sont contenues dans l'intervalle -2^63..2^63-1 (Int64 et LongInt sur les plates-formes iOS 64 bits), la variable est stockée sous la forme d'un quadruple mot signé.
  • Si les deux bornes sont contenues dans l'intervalle 0..2^64-1 (UInt64 et LongWord sur les plates-formes iOS 64 bits), la variable est stockée sous la forme d'un quadruple mot non signé.
Remarque: Un "word" occupe deux octets.

Types caractères

Sur les plates-formes 32 bits et 64 bits :

  • Char etWideChar sont stockés sous la forme d'une variable d'un mot non signé, en utilisant normalement l'encodage UTF-16 ou Unicode.
  • Le type AnsiChar est stocké 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 un comptage 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.

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 type 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 peuvent prendre les valeurs 0 (False) ou toute valeur différente de zéro (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é dans l'état {$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

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 32 bits et 64 bits, un nombre Real48 sur 6 octets (48 bits) est décomposé en trois champs :

1

           39                                 

     8     

s

          f

    e


Si 0 < e <= 255, la valeur v du nombre est donnée par la formule :

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 (Inf). 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

Sur les plates-formes 32 bits et 64 bits, un nombre Single sur 4 octets (32 bits) est décomposé en trois champs :

1

     8     

           23           

s

     e

           f


La valeur v du nombre est donnée par la formule :

  • Si 0 < e < 255, puis v = (-1)s * 2(e-127) * (1.f)
  • Si e = 0 et f <> 0, alors v = (-1)s * 2(-126) * (0.f)
  • Si e = 0 et f = 0, alors v = (-1)s * 0
  • Si e = 255 et f = 0, alors v = (-1)s * Inf
  • Si e = 255 et f <> 0, alors v est un NaN

Le type Double

Le type Real est équivalent, dans son implémentation actuelle, au type Double.

Sur les plates-formes 32 bits et 64 bits, un nombre Double sur 8 octets (64 bits) est décomposé en trois champs.

1

      11      

                           52                           

s

      e

                           f


La valeur v du nombre est donnée par la formule :

  • Si 0 < e < 2047, alors v = (-1)s * 2(e-1023) * (1.f)
  • Si e = 0 et f <> 0, alors v = (-1)s * 2(-1022) * (0.f)
  • Si e = 0 et f = 0, alors v = (-1)s * 0
  • Si e = 2047 and f = 0, alors v = (-1)s * Inf
  • Si e = 2047 and f <> 0, alors v est un NaN.

Le type Extended

Extended offre une plus grande précision sur la plate-forme Intel 32 bits que les autres types réels, mais il est moins portable. Evitez d'utiliser Extended si vous créez des fichiers de données qui doivent être partagés sur plusieurs plates-formes. Faites attention :

Sur la plate-forme Intel 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                                  

s

         e

i

                

                 f

La valeur v du nombre est donnée par la formule :

  • Si 0 <= e < 32767, alors v = (-1)s * 2(e-16383) * (i.f)
  • Si e = 32767 et f = 0, alors v = (-1)s * Inf
  • Si e = 32767 et f <> 0, alors v est un NaN

Sur la plate-forme Intel 64 bits et la plate-forme ARM, le type Extended est un alias pour Double, qui est seulement de 8 octets. Cette différence peut avoir une incidence sur la précision numérique dans les opérations à virgule flottante. Pour de plus amples informations, voir Considérations Delphi pour les applications multi-périphériques. Sur les systèmes macOS, la taille de Extended est désormais de 16 octets pour être compatible avec BCCOSX.

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 ShortString occupe le nombre d'octets correspondant à sa longueur maximale plus un. Le premier octet contient la longueur dynamique en cours 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îne longue

Une variable chaîne de type UnicodeString ou AnsiString occupe 4 octets de mémoire sur les plates-formes 32 bits (et 8 octets sur 64 bits) 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 à 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. Les tableaux suivants présentent l'organisation d'un bloc mémoire de chaîne longue.

Format du type de données UnicodeString (32 bits et 64 bits)

Champ Page de code Taille d'élément Compteur de références Longueur Données chaîne

(dimensionnées en éléments)

Terme Null

Décalage

-12

-10

-8

-4

0..(Longueur - 1)

Longueur * Taille d'élément

Contenu

Page de code 16 bits de données chaîne

Taille d'élément 16 bits de données chaîne

Compte de références sur 32 bits

Longueur en caractères

Chaîne de caractères de données dimensionnées en éléments

Caractère NULL

Les nombres de la ligne Décalage montrent les décalages de champs, décrivant le contenu de la chaîne, du pointeur de chaîne, qui pointe sur le champ Données chaîne (offset = 0), contenant un bloc de mémoire qui contient les valeurs de la chaîne réelle.

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 terminée par null.

Voir aussi "Nouveau type de chaîne : UnicodeString."

Pour les littéraux chaîne, le compilateur génère un bloc mémoire de même organisation 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. Les deux pointeurs, source et destination, pointeront sur le même bloc mémoire après l'assignation.

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 n'est 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îne étendue

Sur les plates-formes 32 bits, une variable chaîne étendue occupe 4 octets de mémoire (et 8 octets sur 64 bits) qui contiennent un pointeur sur une chaîne allouée dynamiquement. Lorsqu'une variable chaîne étendue est vide (contient une chaîne de longueur zéro), le pointeur de chaîne est nil et aucune mémoire dynamique n'est associée à 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 l'organisation d'un bloc mémoire de chaîne étendue sur Windows.

Organisation en mémoire dynamique d'une chaîne étendue (32 bits et 64 bits)

Décalage

-4

0..(Longueur - 1)

Longueur

Contenu

Indicateur de longueur sur 32 bits
(en octets)

Chaîne de caractères

Caractère NULL

La longueur de la chaîne est évaluée en octets, c'est donc le double du nombre de caractères étendus 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 terminée par null.

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

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

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 dépendant de la plate-forme ou si le programme contient du code qui utilise l'adresse de l'ensemble.

Types tableau statique

La mémoire occupée par une variable de tableau statique est définie en calculant Length(array) * SizeOf(array[Low(array)]) en octets. La variable de tableau statique est entièrement allouée en tant que structure des données parent. Un tableau statique est stocké sous la forme d'une séquence contiguë d'éléments du type composant du tableau. Les composants avec les plus faibles index 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 tableau dynamique

Sur la plate-forme 32 bits, une variable tableau dynamique occupe 4 octets de mémoire (et 8 octets sur 64 bits) qui contiennent un pointeur sur le tableau alloué dynamiquement. Quand la variable est vide (non initialisée) ou contient un tableau de longueur zéro, 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 (64 bits sur Win64) et un compte de références sur 32 bits. Le tableau suivant indique l'organisation du bloc de mémoire de tableau dynamique.

Organisation en mémoire d'un tableau dynamique (32 bits et 64 bits)

Décalage 32 bits

-8

-4

0..(Longueur * Taille d'élément - 1)

Décalage 64 bits

-12

-8

0..(Longueur * Taille d'élément - 1)

Contenu

Compte de références sur 32 bits


Indicateur de longueur
sur 32 bits ou 64 bits sur la plate-forme 64 bits (nombre d'éléments)

Eléments du tableau

Types enregistrement

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 (32 bits 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îne courte

1

Types tableau

Identique au type des éléments du tableau

Types enregistrement

Le plus grand alignement des champs de l'enregistrement

Types ensemble

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.

Lorsqu'un type enregistrement est déclaré dans l'état {$A-} ou lorsque la déclaration comprend le modificateur packed, les champs de l'enregistrement ne sont pas alignés, mais des déplacements consécutifs leur sont plutôt affecté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

Les types fichier sont représentés sous forme d'enregistrements. Les fichiers typés et non typés occupent 592 octets sur les plates-formes 32 bits et 616 octets sur les plates-formes 64 bits, disposés comme suit :

 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;

Les fichiers texte occupent 730 octets sur Win32 et 754 octets sur Win64, disposés comme suit :

 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 contient le handle du fichier (quand celui-ci est ouvert).

Le champ Mode peut prendre l'une des valeurs suivantes :

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

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é assigné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 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. Pour davantage d'informations, 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 utiliser le type UnicodeString (le type de chaîne Delphi par défaut), il est plus utile de recourir aux différents types de flux dans l'unité Classes (TFileStream, TStreamReader, TStreamWriter, etc.), car les types de fichiers plus anciens ont une fonctionnalité Unicode limitée, notamment l'ancien type de fichier texte.

Types procéduraux

Sur la plate-forme 32 bits, 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.

Sur la plate-forme 64 bits, un pointeur de procédure est stocké sous la forme d'un pointeur 64 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 64 bits sur le point d'entrée d'une méthode, suivi par un pointeur 64 bits sur un objet.

Types classe

Sur les plates-formes 32 bits (Win32, macOS, iOS et Android), une valeur de type classe est stockée sous la forme d'un pointeur 32 bits sur une instance de la classe (et d'un pointeur 64 bits sur la plate-forme 64 bits), qui est appelée un objet. Le format de données interne d'un objet est similaire à celui d'un enregistrement. Les champs de l'objet sont stockés dans l'ordre de déclaration sous la forme d'une séquence de variables contiguës. Les champs sont toujours alignés, correspondant à un type enregistrement décompacté. Par conséquent, l'alignement correspond au plus grand alignement des champs dans l'objet. Les champs hérités d'une classe ancêtre sont stockés avant les nouveaux champs définis dans la classe descendante.

Sur les plates-formes 32 bits, le premier champ sur 4 octets de chaque objet (le premier champ sur 8 octets sur la plate-forme 64 bits) est 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 VMT sont construites automatiquement par le compilateur et ne sont jamais manipulées directement par un programme. Les pointeurs sur les VMT, 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.

L'organisation d'une VMT est présentée dans le tableau ci-dessous. Sur les plates-formes 32 bits, aux décalages positifs, une VMT se compose d'une liste de pointeurs de méthodes sur 32 bits (pointeurs de méthodes sur 64 bits sur la plate-forme 64 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 organisation est compatible avec une v-table C++ ainsi qu'avec COM. Aux décalages négatifs, une VMT contient un certain nombre de champs qui sont internes à l'implémentation de Delphi. Les applications doivent utiliser les méthodes définies dans TObject pour interroger ces informations, car cette organisation pourra être amenée à changer dans les versions futures du langage Delphi.

Disposition des tables de méthodes virtuelles

Décalage
Win32, macOS
Décalage
Win64
Décalage
iOS/ARM, Android/ARM
Type Description Constante dans System.pas

-88

-200

-108

Pointeur

Pointeur sur la table des méthodes virtuelles (ou nil)

vmtSelfPtr

-84

-192

-104

Pointeur

Pointeur sur la table d'interface (ou nil)

vmtIntfTable

-80

-184

-100

Pointeur

Pointeur sur la table des informations d'automation (ou nil)

vmtAutoTable

-76

-176

-96

Pointeur

Pointeur sur la table d'initialisation d'instance (ou nil)

vmtInitTable

-72

-168

-92

Pointeur

Pointeur sur la table des informations de type (ou nil)

vmtTypeInfo

-68

-160

-88

Pointeur

Pointeur sur la table de définition des champs (ou nil)

vmtFieldTable

-64

-152

-84

Pointeur

Pointeur sur la table de définition des méthodes (ou nil)

vmtMethodTable

-60

-144

-80

Pointeur

Pointeur sur la table des méthodes dynamiques (ou nil)

vmtDynamicTable

-56

-136

-76

Pointeur

Pointeur sur une chaîne courte contenant le nom de la classe

vmtClassName

-52

-128

-72

Cardinal

Taille de l'instance en octets

vmtInstanceSize

-48

-120

-68

Pointeur

Pointeur sur un pointeur vers la classe ancêtre (ou nil)

vmtParent

n/a

n/a

-64

Pointeur

Point d'entrée de la méthode __ObjAddRef

vmtObjAddRef

n/a

n/a

-60

Pointeur

Point d'entrée de la méthode __ObjRelease

vmtObjRelease

-44

-112

-56

Pointeur

Point d'entrée de la méthode Equals

vmtEquals

-40

-104

-52

Pointeur

Point d'entrée de la méthode GetHashCode

vmtGetHashCode

-36

-96

-48

Pointeur

Point d'entrée de la méthode ToString

vmtToString

-32

-88

-44

Pointeur

Pointeur sur le point d'entrée de la méthode SafecallException (ou nil)

vmtSafeCallException

-28

-80

-40

Pointeur

Point d'entrée de la méthode AfterConstruction

vmtAfterConstruction

-24

-72

-36

Pointeur

Point d'entrée de la méthode BeforeDestruction

vmtBeforeDestruction

-20

-64

-32

Pointeur

Point d'entrée de la méthode Dispatch

vmtDispatch

-16

-56

-28

Pointeur

Point d'entrée de la méthode DefaultHandler

vmtDefaultHandler

-12

-48

-24

Pointeur

Point d'entrée de la méthode NewInstance

vmtNewInstance

-8

-40

-20

Pointeur

Point d'entrée de la méthode FreeInstance

vmtFreeInstance

-4

-32

-16

Pointeur

Point d'entrée du destructeur Destroy

vmtDestroy

0

0

0

Pointeur

Point d'entrée de la première méthode virtuelle définie par l'utilisateur

4

8

4

Pointeur

Point d'entrée de la deuxième méthode virtuelle définie par l'utilisateur

Types référence de classe

Sur la plate-forme 32 bits (Win32, macOS, iOS et Android), 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.

Sur la plate-forme 64 bits (Win64), une valeur référence de classe est stockée sous la forme d'un pointeur 64 bits sur la table des méthodes virtuelles (VMT) d'une classe.

Types variants

Les variants invoquent la conversion boxing et unboxing des données dans un wrapper d'objet, ainsi que les classes d'assistance de Delphi pour implémenter les fonctions RTL associées aux variants.

Sur la plate-forme 32 bits, 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. Sur la plate-forme 64 bits, un variant est stocké sous la forme d'un enregistrement de 24 octets. Les unités System et System.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 :

  • Le champ VType du type TVarType a la taille Word (16 bits). VType contient le code de type du variant dans ses 12 bits de poids faible (les bits définis par la constante varTypeMask = $FFF). De plus, le bit varArray = $2000 peut être défini pour indiquer que le variant est un tableau, et le bit varByRef (= $4000) peut être défini pour indiquer que le variant contient une référence (et non une valeur).
  • Les champs Reserved1, Reserved2 et Reserved3 (taille Word) sont inutilisés.

Le contenu des 8 octets (plate-forme 32 bits) ou 16 octets (plate-forme 64 bits) restants d'un enregistrement TVarData dépend du champ VType comme suit :

  • Si aucun des bits varArray et varByRef n'est défini, le variant contient une valeur du type donné.
  • Si le bit varArray est défini, le variant contient un pointeur sur une structure TVarArray qui définit un tableau. Le type de chaque élément de tableau est donné par les bits varTypeMask dans le champ VType.
  • 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 de type varString est privé. Les variants contenant une valeur varString ne doivent jamais être transmis à une fonction non Delphi. Sur la plate-forme Windows, le support Automation de Delphi effectue automatiquement la conversion des variants varString en variants varOleStr avant de les transmettre sous forme de paramètres aux fonctions externes.

Voir aussi