C++
Layout dei tipi di oggetto
Ricerca…
Osservazioni
Vedi anche Dimensione dei tipi interi .
Tipi di classe
Per "classe", intendiamo un tipo che è stato definito usando la parola chiave class
o struct
(ma non enum struct
o enum class
).
Anche una classe vuota occupa ancora almeno un byte di spazio; consisterà quindi esclusivamente di imbottitura. Ciò garantisce che se
p
punta a un oggetto di una classe vuota, allorap + 1
è un indirizzo distinto e punta a un oggetto distinto. Tuttavia, è possibile che una classe vuota abbia una dimensione pari a 0 quando viene utilizzata come classe base. Vedi l' ottimizzazione di base vuota .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 rappresentazione dell'oggetto di un tipo di classe contiene le rappresentazioni di oggetti della classe base e dei tipi di membri non statici. Pertanto, ad esempio, nella seguente classe:
struct S { int x; char* y; };
c'è una sequenza consecutiva di
sizeof(int)
byte all'interno di un oggettoS
, chiamato subobject, che contiene il valore dix
, e un altro subobject consizeof(char*)
byte che contiene il valore diy
. I due non possono essere intercalati.Se un tipo di classe ha membri e / o classi base con tipi
t1, t2,...tN
, la dimensione deve essere almenosizeof(t1) + sizeof(t2) + ... + sizeof(tN)
dati i punti precedenti . Tuttavia, a seconda dei requisiti di allineamento dei membri e delle classi di base, il compilatore può essere costretto a inserire il padding tra i sottooggetti, o all'inizio o alla fine dell'oggetto completo.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).
Se il riempimento è inserito in un oggetto a causa dei requisiti di allineamento, la dimensione sarà maggiore della somma delle dimensioni dei membri e delle classi base. Con l'allineamento
n
byte, la dimensione sarà in genere il multiplo più piccolo din
che è più grande della dimensione di tutti i membri e le classi base. Ogni membromemN
verrà tipicamente collocato in un indirizzo che è un multiplo dialignof(memN)
, en
sarà tipicamente il più grandealignof
di tutti glialignof
dei membri. A causa di ciò, se un membro con unalignof
piùalignof
è seguito da un membro con unalignof
piùalignof
, esiste la possibilità che quest'ultimo non sia allineato correttamente se posto immediatamente dopo il primo. In questo caso, il riempimento (noto anche come membro di allineamento ) verrà posizionato tra i due membri, in modo che quest'ultimo possa avere l'allineamento desiderato. Viceversa, se un membro con unalignof
piùalignof
è seguito da un membro con unalignof
piùalignof
, di solito non sarà necessario alcun riempimento. Questo processo è anche noto come "imballaggio".
A causa delle classi che in genere condividono l'alignof
del loro membro con l'alignof
più grande, le classi saranno in genere allineate con l'alignof
del tipo più grande incorporato che contengono direttamente o indirettamente.// 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))
Se l'allineamento rigoroso viene forzato con gli
alignas
, verrà utilizzato il riempimento per forzare il tipo a soddisfare l'allineamento specificato, anche quando altrimenti sarebbe più piccolo. Ad esempio, con la definizione seguente,Chars<5>
avrà tre (o forse più) byte di riempimento inseriti alla fine in modo che la sua dimensione totale sia 8. Non è possibile per una classe con un allineamento di 4 avere una dimensione di 5 perché sarebbe impossibile creare una matrice di quella classe, quindi la dimensione deve essere "arrotondata" a un multiplo di 4 inserendo i byte di riempimento.// 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");
- Se due membri non statici di una classe hanno lo stesso identificatore di accesso , allora quello che viene dopo in ordine di dichiarazione è garantito per venire successivamente nella rappresentazione dell'oggetto. Ma se due membri non statici hanno identificatori di accesso diversi, il loro ordine relativo all'interno dell'oggetto non è specificato.
- Non è specificato l'ordine in cui gli oggetti secondari della classe base vengono visualizzati all'interno di un oggetto, sia che si verifichino consecutivamente, sia se compaiono prima, dopo o tra sottooggetti membri.
Tipi aritmetici
Tipi di caratteri stretti
Il tipo di unsigned char
utilizza tutti i bit per rappresentare un numero binario. Pertanto, ad esempio, se il unsigned char
è lungo 8 bit, i 256 pattern di bit possibili di un oggetto char
rappresentano i 256 diversi valori {0, 1, ..., 255}. Il numero 42 è garantito per essere rappresentato dal modello di bit 00101010
.
Il tipo di signed char
non ha bit di riempimento, cioè, se il signed char
è lungo 8 bit, allora ha 8 bit di capacità per rappresentare un numero.
Nota che queste garanzie non si applicano a tipi diversi dai tipi di caratteri stretti.
Tipi interi
I tipi interi senza segno usano un sistema binario puro, ma possono contenere bit di riempimento. Ad esempio, è possibile (anche se improbabile) che l' unsigned int
abbia una lunghezza di 64 bit, ma sia in grado di memorizzare interi compresi tra 0 e 2 32 - 1, inclusi. Gli altri 32 bit sarebbero i bit di riempimento, che non dovrebbero essere scritti direttamente.
I tipi interi con segno usano un sistema binario con un bit di segno ed eventualmente dei bit di riempimento. I valori che appartengono all'intervallo comune di un tipo di intero con segno e il tipo di intero senza segno corrispondente hanno la stessa rappresentazione. Ad esempio, se il modello di bit 0001010010101011
di un oggetto unsigned short
rappresenta il valore 5291
, quindi rappresenta anche il valore 5291
quando viene interpretato come oggetto short
.
È definito dall'implementazione se viene utilizzato il complemento a due, il complemento a un altro o la rappresentazione a livello di segno, poiché tutti e tre i sistemi soddisfano i requisiti del paragrafo precedente.
Tipi di virgola mobile
La rappresentazione del valore dei tipi a virgola mobile è definita dall'implementazione. Più comunemente, i tipi float
e double
sono conformi a IEEE 754 e sono lunghi 32 e 64 bit (quindi, ad esempio, float
avrebbe 23 bit di precisione che seguiranno 8 bit esponenziali e 1 bit di segno). Tuttavia, lo standard non garantisce nulla. I tipi a virgola mobile hanno spesso "rappresentazioni di trap", che causano errori quando vengono utilizzati nei calcoli.
Array
Un tipo di array non ha padding tra i suoi elementi. Pertanto, una matrice con elemento di tipo T
è solo una sequenza di oggetti T
disposti in memoria, nell'ordine.
Una matrice multidimensionale è una matrice di matrici e la suddetta si applica in modo ricorsivo. Ad esempio, se abbiamo la dichiarazione
int a[5][3];
quindi a
è una matrice di 5 matrici di 3 int
s. Pertanto, a[0]
, che consiste dei tre elementi a[0][0]
, a[0][1]
, a[0][2]
, è disposto in memoria prima di a[1]
, che consiste di a[1][0]
, a[1][1]
e a[1][2]
. Questo è chiamato ordine principale di riga .