Formats de données internes

De RAD Studio (Français)

Remonter à Gestion de la mémoire - Index

Les rubriques suivantes décrivent les formats internes des types de données Delphi :

Sommaire

Types entiers

Le format d'une variable de type 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 "word" 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. Une valeur réelle a la forme :

+/- mantisse * 2^exposant

où la mantisse ne possède qu'un bit à gauche de la virgule décimale binaire (C'est-à-dire que 0 <= mantisse < 2).

Dans les schémas suivants, 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 moins significatif du dernier octet.

Type Real48

Sur la plate-forme Win32, 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 vdu 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 permet pas de 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.

Type Single

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, alors 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

Type Double

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 et f = 0, alors v = (-1)s * Inf
  • Si e = 2047 et f <> 0, alors v est un NaN

Type Extended

Un nombre Extended sur 10 octets (80 bits) est décomposé 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

Type Comp

Un nombre Comp sur 8 octets (64 bits) est stocké comme un entier signé sur 64 bits.

Type Currency

Un nombre Currency de 8 octets (64 bits) est stocké sous la forme d'un entier scalaire et signé de 64 bits dont les quatre chiffres les moins significatifs représentent implicitement les quatre chiffres après la virgule.

Types pointeur

Un type pointeur est stocké dans 4 octets sous la forme d'une adresse 32 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 maximum 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 maximum d'une chaîne 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 quatre octets de mémoire, qui contiennent un pointeur sur une chaîne allouée dynamiquement. Lorsqu'une variable chaîne 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 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) :

Offset 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 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.

Le compilateur génère un bloc mémoire de même implantation qu'une chaîne allouée dynamiquement pour des constantes et littéraux chaînes, mais avec un compteur de références de -1. Lorsqu'une variable chaîne est affectée à une constante chaîne, le pointeur de chaîne se voit affecter l'adresse du bloc mémoire généré pour la constante chaîne. Les routines intégrées de gestion de chaîne savent qu'elles ne doivent pas essayer de modifier des blocs ayant un compteur de références de -1.

Types chaînes étendues

Sur Win32, une variable chaîne étendue occupe quatre octets de mémoire et contient 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 ensembles

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 maximum 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 égal à (Max div 8) (Min div 8) + 1, où Max et Min sont les limites supérieure et inférieure du type de base de l'ensemble. Le nombre d'octets d'un élément spécifique E est (E div 8) (Min div 8) et le nombre de bits dans cet octet est E mod 8, où E indique 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 Integer 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'indices 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 quatre 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

éléments du tableau

Types enregistrements

Lorsqu'un type enregistrement est déclaré dans l'état {$A+} (valeur par défaut) et lorsque la déclaration ne comprend pas de modificateur packed, le type est un enregistrement décompacté, et les champs de l'enregistrement sont alignés pour faciliter l'accès par la CPU. L'alignement est contrôlé par le type de chaque champ et sur le fait que les champs sont déclarés ensemble ou non. 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 scalaires

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'à trois 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.

Si deux champs partagent une spécification de type commune, ils sont compactés même si la déclaration ne comprend 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 :

type
  TMyRecord = record
    A, B: Extended;
    C: Extended;
  end;

A et B sont compactés (alignés sur les limites en octets) car ils partagent la même spécification de type. Le compilateur complète la structure avec des octets inutilisés pour s'assurer que C 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 compacté est simplement la taille de tous les champs. Comme l'alignement des données peut changer, c'est une bonne idée de compacter toute structure 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, répartis 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, répartis 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 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é 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 ; il s'agit d'une suite 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'indice 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. Les indicateurs déterminent le style de rupture de ligne comme suit :

bit 0 vide

rupture de ligne LF

bit 0 défini

rupture de ligne CRLF

Tous les autres bits indicateurs 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 de classes

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 quatre 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 VMT sont construites automatiquement par le compilateur et ne sont jamais manipulées directement par un programme. De même, les pointeurs sur les VMT qui sont stockés automatiquement dans les objets par la méthode constructeur 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 :

  • VType contient le code du type du variant dans ses douze bits de poids faible (les bits définis par la constante varTypeMask). De plus, le bit varArray peut être initialisé pour indiquer que le variant est un tableau, et le bit varByRef peut être initialisé 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 huit octets restants d'un enregistrement TVarData dépend du champ VType comme suit :

  • Si aucun des bits varArray ou varByRef ne sont initialisés, le variant contient une valeur du type donné.
  • Si le bit varArray est défini, le variant contient un pointeur sur une structure TVarArray définissant le tableau. Le type de chaque élément du tableau est donné par les bits varTypeMask du 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.

Notez que le code 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.

Voir aussi

Autres langues