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.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow