C++
Disposition des types d'objet
Recherche…
Remarques
Voir aussi Taille des types intégraux .
Types de classes
Par «classe», nous entendons un type défini avec le mot struct
clé class
ou struct
(mais pas enum struct
ou enum class
).
Même une classe vide occupe au moins un octet de stockage; il s'agira donc uniquement de rembourrage. Cela garantit que si
p
pointe sur un objet d'une classe vide, alorsp + 1
est une adresse distincte et pointe vers un objet distinct. Cependant, il est possible qu'une classe vide ait une taille de 0 lorsqu'elle est utilisée comme classe de base. Voir optimisation de base vide .class Empty_1 {}; // sizeof(Empty_1) == 1 class Empty_2 {}; // sizeof(Empty_2) == 1 class Derived : Empty_1 {}; // sizeof(Derived) == 1 class DoubleDerived : Empty_1, Empty_2 {}; // sizeof(DoubleDerived) == 1 class Holder { Empty_1 e; }; // sizeof(Holder) == 1 class DoubleHolder { Empty_1 e1; Empty_2 e2; }; // sizeof(DoubleHolder) == 2 class DerivedHolder : Empty_1 { Empty_1 e; }; // sizeof(DerivedHolder) == 2
La représentation d'objet d'un type de classe contient les représentations d'objet de la classe de base et des types de membres non statiques. Par exemple, dans la classe suivante:
struct S { int x; char* y; };
il existe une séquence consécutive de
sizeof(int)
octetssizeof(int)
dans un objetS
, appelée sous - objet, qui contient la valeur dex
, et un autre sous-objet desizeof(char*)
octetssizeof(char*)
contenant la valeur dey
. Les deux ne peuvent pas être entrelacés.Si un type de classe a des membres et / ou des classes de base avec les types
t1, t2,...tN
, la taille doit être au minimumsizeof(t1) + sizeof(t2) + ... + sizeof(tN)
compte tenu des points précédents . Toutefois, en fonction des exigences d' alignement des membres et des classes de base, le compilateur peut être contraint d'insérer un remplissage entre les sous-objets ou au début ou à la fin de l'objet complet.struct AnInt { int i; }; // sizeof(AnInt) == sizeof(int) // Assuming a typical 32- or 64-bit system, sizeof(AnInt) == 4 (4). struct TwoInts { int i, j; }; // sizeof(TwoInts) >= 2 * sizeof(int) // Assuming a typical 32- or 64-bit system, sizeof(TwoInts) == 8 (4 + 4). struct IntAndChar { int i; char c; }; // sizeof(IntAndChar) >= sizeof(int) + sizeof(char) // Assuming a typical 32- or 64-bit system, sizeof(IntAndChar) == 8 (4 + 1 + padding). struct AnIntDerived : AnInt { long long l; }; // sizeof(AnIntDerived) >= sizeof(AnInt) + sizeof(long long) // Assuming a typical 32- or 64-bit system, sizeof(AnIntDerived) == 16 (4 + padding + 8).
Si le remplissage est inséré dans un objet en raison des exigences d'alignement, la taille sera supérieure à la somme des tailles des membres et des classes de base. Avec l'alignement sur
n
octets, la taille sera généralement le plus petit multiple den
supérieur à la taille de tous les membres et classes de base. Chaque membrememN
sera généralement placé à une adresse multiple dealignof(memN)
, etn
sera généralement le plus grandalignof
de tous lesalignof
des membres. De ce fait, si un membre avec unalignof
plusalignof
est suivi par un membre avec unalignof
plusalignof
, il est possible que ce dernier membre ne soit pas correctement aligné s'il est placé immédiatement après le premier. Dans ce cas, un rembourrage (également appelé élément d'alignement ) sera placé entre les deux éléments, de sorte que ce dernier membre puisse avoir son alignement souhaité. Inversement, si un membre avec unalignof
plusalignof
est suivi par un membre avec unalignof
plusalignof
, aucun remplissage ne sera généralement nécessaire. Ce processus est également appelé "emballage".
Étant donné que les classes partageant généralement l'alignof
de leur membre avec l'alignof
le plusalignof
, les classes seront généralement alignées sur l'alignof
du plus grand type intégré qu'elles contiennent directement ou indirectement.// Assume sizeof(short) == 2, sizeof(int) == 4, and sizeof(long long) == 8. // Assume 4-byte alignment is specified to the compiler. struct Char { char c; }; // sizeof(Char) == 1 (sizeof(char)) struct Int { int i; }; // sizeof(Int) == 4 (sizeof(int)) struct CharInt { char c; int i; }; // sizeof(CharInt) == 8 (1 (char) + 3 (padding) + 4 (int)) struct ShortIntCharInt { short s; int i; char c; int j; }; // sizeof(ShortIntCharInt) == 16 (2 (short) + 2 (padding) + 4 (int) + 1 (char) + // 3 (padding) + 4 (int)) struct ShortIntCharCharInt { short s; int i; char c; char d; int j; }; // sizeof(ShortIntCharCharInt) == 16 (2 (short) + 2 (padding) + 4 (int) + 1 (char) + // 1 (char) + 2 (padding) + 4 (int)) struct ShortCharShortInt { short s; char c; short t; int i; }; // sizeof(ShortCharShortInt) == 12 (2 (short) + 1 (char) + 1 (padding) + 2 (short) + // 2 (padding) + 4 (int)) struct IntLLInt { int i; long long l; int j; }; // sizeof(IntLLInt) == 16 (4 (int) + 8 (long long) + 4 (int)) // If packing isn't explicitly specified, most compilers will pack this as // 8-byte alignment, such that: // sizeof(IntLLInt) == 24 (4 (int) + 4 (padding) + 8 (long long) + // 4 (int) + 4 (padding)) // Assume sizeof(bool) == 1, sizeof(ShortIntCharInt) == 16, and sizeof(IntLLInt) == 24. // Assume default alignment: alignof(ShortIntCharInt) == 4, alignof(IntLLInt) == 8. struct ShortChar3ArrShortInt { short s; char c3[3]; short t; int i; }; // ShortChar3ArrShortInt has 4-byte alignment: alignof(int) >= alignof(char) && // alignof(int) >= alignof(short) // sizeof(ShortChar3ArrShortInt) == 12 (2 (short) + 3 (char[3]) + 1 (padding) + // 2 (short) + 4 (int)) // Note that t is placed at alignment of 2, not 4. alignof(short) == 2. struct Large_1 { ShortIntCharInt sici; bool b; ShortIntCharInt tjdj; }; // Large_1 has 4-byte alignment. // alignof(ShortIntCharInt) == alignof(int) == 4 // alignof(b) == 1 // Therefore, alignof(Large_1) == 4. // sizeof(Large_1) == 36 (16 (ShortIntCharInt) + 1 (bool) + 3 (padding) + // 16 (ShortIntCharInt)) struct Large_2 { IntLLInt illi; float f; IntLLInt jmmj; }; // Large_2 has 8-byte alignment. // alignof(IntLLInt) == alignof(long long) == 8 // alignof(float) == 4 // Therefore, alignof(Large_2) == 8. // sizeof(Large_2) == 56 (24 (IntLLInt) + 4 (float) + 4 (padding) + 24 (IntLLInt))
Si l'alignement strict est forcé avec
alignas
, le remplissage sera utilisé pour forcer le type à respecter l'alignement spécifié, même s'il serait autrement plus petit. Par exemple, avec la définition ci-dessous, les caractèresChars<5>
auront trois (ou peut-être plus) octets de remplissage insérés à la fin de sorte que leur taille totale soit 8. Il n'est pas possible pour une classe avec un alignement de 4 d'avoir une taille de 5 car il serait impossible de faire un tableau de cette classe, donc la taille doit être "arrondie" à un multiple de 4 en insérant des octets de remplissage.// This type shall always be aligned to a multiple of 4. Padding shall be inserted as // needed. // Chars<1>..Chars<4> are 4 bytes, Chars<5>..Chars<8> are 8 bytes, etc. template<size_t SZ> struct alignas(4) Chars { char arr[SZ]; }; static_assert(sizeof(Chars<1>) == sizeof(Chars<4>), "Alignment is strict.\n");
- Si deux membres non statiques d'une classe ont le même spécificateur d'accès , celui qui apparaîtra plus tard dans l'ordre de déclaration sera garanti plus tard dans la représentation de l'objet. Mais si deux membres non statiques ont des spécificateurs d'accès différents, leur ordre relatif dans l'objet n'est pas spécifié.
- Il n'est pas spécifié dans quel ordre les sous-objets de classe de base apparaissent dans un objet, qu'ils soient consécutifs ou non, qu'ils apparaissent avant, après ou entre les sous-objets membres.
Types arithmétiques
Types de caractères étroits
Le type de caractère unsigned char
utilise tous les bits pour représenter un nombre binaire. Par conséquent, par exemple, si le caractère unsigned char
longueur de 8 bits, alors les 256 modèles de bits possibles d'un objet char
représentent les 256 valeurs différentes {0, 1, ..., 255}. Le nombre 42 est garanti pour être représenté par le modèle de bit 00101010
.
Le type de caractère signed char
n'a pas de bits de remplissage, c'est -à- dire que si le caractère signed char
longueur de 8 bits, il a une capacité de 8 bits pour représenter un nombre.
Notez que ces garanties ne s'appliquent pas aux types autres que les types de caractères étroits.
Types entiers
Les types entiers non signés utilisent un système binaire pur, mais peuvent contenir des bits de remplissage. Par exemple, il est possible (mais improbable) que unsigned int
ait une longueur de 64 bits, mais soit uniquement capable de stocker des entiers compris entre 0 et 2 32 - 1 inclus. Les 32 autres bits seraient des bits de remplissage, auxquels il ne faut pas écrire directement.
Les types entiers signés utilisent un système binaire avec un bit de signe et éventuellement des bits de remplissage. Les valeurs qui appartiennent à la plage commune d'un type d'entier signé et au type d'entier non signé correspondant ont la même représentation. Par exemple, si le modèle de bit 0001010010101011
d'un objet unsigned short
représente la valeur 5291
, il représente également la valeur 5291
lorsqu'il est interprété comme un objet short
.
Il est défini par la mise en œuvre si une représentation à deux, un complément ou une magnitude de signe est utilisée, car les trois systèmes satisfont aux exigences du paragraphe précédent.
Virgule flottante
La représentation de la valeur des types à virgule flottante est définie par l'implémentation. Le plus souvent, les types float
et double
sont conformes à IEEE 754 et ont une longueur de 32 et 64 bits (par exemple, float
aurait 23 bits de précision qui suivraient 8 bits d’exposant et 1 bit de signe). Cependant, la norme ne garantit rien. Les types à virgule flottante ont souvent des "représentations d'interruptions", qui provoquent des erreurs lorsqu'elles sont utilisées dans les calculs.
Tableaux
Un type de tableau n'a pas de remplissage entre ses éléments. Par conséquent, un tableau avec le type d'élément T
n'est qu'une séquence d'objets T
disposés en mémoire, dans l'ordre.
Un tableau multidimensionnel est un tableau de tableaux, et ce qui précède s'applique de manière récursive. Par exemple, si nous avons la déclaration
int a[5][3];
alors a
est un tableau de 5 tableaux de 3 int
s. Par conséquent, a[0]
, qui consiste en trois éléments a[0][0]
, a[0][1]
, a[0][2]
, est mis en mémoire avant a[1]
, qui consiste à de a[1][0]
, a[1][1]
et a[1][2]
. Ceci s'appelle ordre majeur de rangée .