C Language
Imbottitura e imballaggio della struttura
Ricerca…
introduzione
Per impostazione predefinita, i compilatori C dispongono di strutture in modo che sia possibile accedere rapidamente a ciascun membro, senza incorrere in penalità per l'accesso non allineato, un problema con le macchine RISC come DEC Alpha e alcune CPU ARM.
A seconda dell'architettura della CPU e del compilatore, una struttura può occupare più spazio in memoria della somma delle dimensioni dei membri del componente. Il compilatore può aggiungere padding tra membri o alla fine della struttura, ma non all'inizio.
L'imballaggio sovrascrive il riempimento predefinito.
Osservazioni
Eric Raymond ha un articolo su The Lost Art of C Structure Packing che è una lettura utile.
Strutture di imballaggio
Di default le strutture sono riempite in C. Se vuoi evitare questo comportamento, devi richiederlo esplicitamente. Sotto GCC è __attribute__((__packed__)) . Considera questo esempio su una macchina a 64 bit:
struct foo {
char *p; /* 8 bytes */
char c; /* 1 byte */
long x; /* 8 bytes */
};
La struttura verrà automaticamente riempita per avere 8-byte allineamento di 8-byte e avrà il seguente aspetto:
struct foo {
char *p; /* 8 bytes */
char c; /* 1 byte */
char pad[7]; /* 7 bytes added by compiler */
long x; /* 8 bytes */
};
Quindi sizeof(struct foo) ci darà 24 invece di 17 . Questo è accaduto a causa di un compilatore a 64 bit di lettura / scrittura da / a memoria in 8 byte di parola in ogni passaggio e ovvio quando si tenta di scrivere char c; un byte in memoria un intero 8 byte (cioè una parola) recuperato e consuma solo il primo byte di esso e i suoi sette successivi di byte rimane vuoto e non accessibile per qualsiasi operazione di lettura e scrittura per il riempimento della struttura.
Imballaggio della struttura
Ma se aggiungi l'attributo packed , il compilatore non aggiungerà il padding:
struct __attribute__((__packed__)) foo {
char *p; /* 8 bytes */
char c; /* 1 byte */
long x; /* 8 bytes */
};
Ora sizeof(struct foo) restituirà 17 .
Vengono utilizzate strutture generalmente imballate:
- Per risparmiare spazio
- Formattare una struttura dati per trasmettere su rete senza dipendere da ciascun allineamento dell'architettura di ciascun nodo della rete.
È necessario tenere presente che alcuni processori come ARM Cortex-M0 non consentono l'accesso alla memoria non allineato; in questi casi, l'impacchettamento della struttura può comportare un comportamento indefinito e causare il blocco della CPU.
Imbottitura della struttura
Supponiamo che questa struct sia definita e compilata con un compilatore a 32 bit:
struct test_32 {
int a; // 4 byte
short b; // 2 byte
int c; // 4 byte
} str_32;
Potremmo aspettarci che questa struct occupi solo 10 byte di memoria, ma stampando sizeof(str_32) vediamo che usa 12 byte.
Ciò è accaduto perché il compilatore allinea le variabili per l'accesso rapido. Un modello comune è che quando il tipo di base occupa N byte (dove N è una potenza di 2 come 1, 2, 4, 8, 16 e raramente più grande), la variabile deve essere allineata su un limite di N byte ( un multiplo di N byte).
Per la struttura mostrata con sizeof(int) == 4 e sizeof(short) == 2 , un layout comune è:
-
int a;memorizzato all'offset 0; taglia 4. -
short b;memorizzato all'offset 4; taglia 2. - imbottitura senza nome all'offset 6; taglia 2.
-
int c;memorizzato all'offset 8; taglia 4.
Quindi struct test_32 occupa 12 byte di memoria. In questo esempio, non vi è alcun riempimento finale.
Il compilatore garantirà che tutte le variabili struct test_32 siano memorizzate a partire da un limite di 4 byte, in modo che i membri all'interno della struttura siano correttamente allineati per l'accesso rapido. Le funzioni di allocazione della memoria come malloc() , calloc() e realloc() sono necessarie per garantire che il puntatore restituito sia sufficientemente allineato per l'uso con qualsiasi tipo di dati, quindi anche le strutture allocate dinamicamente saranno allineate correttamente.
È possibile ritrovarsi con situazioni strane come su un processore Intel x86_64 a 64 bit (ad es. Intel Core i7 - un Mac con MacOS Sierra o Mac OS X), dove quando si compila in modalità a 32 bit, i compilatori vengono posizionati in modo double su un Limite di 4 byte; ma, sullo stesso hardware, durante la compilazione in modalità a 64 bit, i compilatori vengono posizionati in modo double su un limite di 8 byte.