Layout della memoria Struct in C


86

Ho uno sfondo C #. Sono un principiante di un linguaggio di basso livello come C.

In C #, structla memoria di è impostata dal compilatore per impostazione predefinita. Il compilatore può riordinare i campi dati o riempire implicitamente bit aggiuntivi tra i campi. Quindi, ho dovuto specificare alcuni attributi speciali per sovrascrivere questo comportamento per il layout esatto.

AFAIK, C non riordina o allinea il layout di memoria di a structper impostazione predefinita. Tuttavia, ho sentito che c'è una piccola eccezione che è molto difficile da trovare.

Qual è il comportamento del layout di memoria di C? Cosa dovrebbe essere riordinato / allineato e non?

Risposte:


111

In C, il compilatore è autorizzato a dettare un certo allineamento per ogni tipo primitivo. In genere l'allineamento è la dimensione del tipo. Ma è interamente specifico per l'implementazione.

I byte di riempimento vengono introdotti in modo che ogni oggetto sia allineato correttamente. Il riordino non è consentito.

Probabilmente ogni moderno compilatore remoto implementa il #pragma packche consente il controllo sul riempimento e lascia al programmatore il rispetto dell'ABI. (Tuttavia, è rigorosamente non standard.)

Da C99 §6.7.2.1:

12 Ogni membro non di campo di bit di una struttura o di un oggetto unione è allineato in un modo definito dall'implementazione appropriato al suo tipo.

13 All'interno di un oggetto struttura, i membri del campo non di bit e le unità in cui risiedono i campi di bit hanno indirizzi che aumentano nell'ordine in cui sono dichiarati. Un puntatore a un oggetto struttura, opportunamente convertito, punta al suo membro iniziale (o se quel membro è un campo di bit, quindi all'unità in cui risiede) e viceversa. Potrebbe esserci un riempimento senza nome all'interno di un oggetto struttura, ma non all'inizio.


1
Alcuni compilatori (ad es. GCC) implementano lo stesso effetto #pragma packma con un controllo più dettagliato sulla semantica.
— Chris Lutz

21
Sono sorpreso di vedere un voto negativo. Qualcuno può sottolineare l'errore?
— Potatoswatter

2
Anche C11 ha _Alignas.
— idmean

117

È specifico dell'implementazione, ma in pratica la regola (in assenza di #pragma packo simili) è:

  • I membri Struct vengono memorizzati nell'ordine in cui vengono dichiarati. (Questo è richiesto dallo standard C99, come menzionato qui in precedenza.)
  • Se necessario, il riempimento viene aggiunto prima di ogni membro della struttura, per garantire il corretto allineamento.
  • Ogni tipo primitivo T richiede un allineamento di sizeof(T)byte.

Quindi, data la seguente struttura:

  • ch1 è all'offset 0
  • un byte di riempimento viene inserito per allineare ...
  • s all'offset 2
  • ch2 è all'offset 4, immediatamente dopo s
  • 3 byte di riempimento vengono inseriti per allineare ...
  • ll all'offset 8
  • i è all'offset 16, subito dopo ll
  • Alla fine vengono aggiunti 4 byte di riempimento in modo che la struttura complessiva sia un multiplo di 8 byte. Ho controllato questo su un sistema a 64 bit: i sistemi a 32 bit possono consentire alle strutture di avere un allineamento a 4 byte.

Quindi sizeof(ST)è 24.

Può essere ridotto a 16 byte riorganizzando i membri per evitare il riempimento:


3
Se necessario, il riempimento viene aggiunto prima ... Di più come dopo. Meglio aggiungere un charmembro finale al tuo esempio.
— Deduplicator

9
Un tipo primitivo non richiede necessariamente un allineamento di sizeof(T)byte. Ad esempio, un valore doublesu architetture comuni a 32 bit è di 8 byte ma spesso richiede solo l'allineamento di 4 byte . Inoltre, il riempimento alla fine della struttura si limita ad allineare il membro più largo della struttura. Ad esempio, una struttura di 3 variabili char potrebbe non avere riempimento.
— Matt

1
@ dan04, sarebbe una buona pratica disporre le strutture in ordine decrescente di sizeof (T). Ci sarebbero degli svantaggi nel farlo?
— RohitMat

11

Puoi iniziare leggendo l' articolo di wikipedia sull'allineamento della struttura dei dati per ottenere una migliore comprensione dell'allineamento dei dati.

Dalla articolo di Wikipedia :

L'allineamento dei dati significa mettere i dati a un offset di memoria uguale a un multiplo della dimensione della parola, il che aumenta le prestazioni del sistema a causa del modo in cui la CPU gestisce la memoria. Per allineare i dati, potrebbe essere necessario inserire alcuni byte privi di significato tra la fine dell'ultima struttura dati e l'inizio della successiva, che è il riempimento della struttura dati.

Da 6.54.8 Pragma di impacchettamento della struttura della documentazione GCC:

Per compatibilità con i compilatori Microsoft Windows, GCC supporta una serie di direttive #pragma che modificano l'allineamento massimo dei membri delle strutture (diversi dai campi di bit di larghezza zero), delle unioni e delle classi definite successivamente. Il valore n sotto deve sempre essere una piccola potenza di due e specifica il nuovo allineamento in byte.

  1. #pragma pack(n) imposta semplicemente il nuovo allineamento.
  2. #pragma pack() imposta l'allineamento a quello che era in vigore all'avvio della compilazione (vedere anche l'opzione della riga di comando -fpack-struct [=] vedere Opzioni di generazione del codice).
  3. #pragma pack(push[,n]) inserisce l'impostazione di allineamento corrente su uno stack interno e quindi imposta facoltativamente il nuovo allineamento.
  4. #pragma pack(pop)ripristina l'impostazione di allineamento a quella salvata nella parte superiore dello stack interno (e rimuove quella voce dello stack). Nota che #pragma pack([n])non influenza questo stack interno; quindi è possibile essere #pragma pack(push) seguito da più #pragma pack(n) istanze e finalizzato da un unico #pragma pack(pop).

Alcuni target, ad esempio i386 e powerpc, supportano ms_struct #pragmache delinea una struttura come documentata __attribute__ ((ms_struct)).

  1. #pragma ms_struct on attiva il layout per le strutture dichiarate.
  2. #pragma ms_struct off disattiva il layout per le strutture dichiarate.
  3. #pragma ms_struct reset torna al layout predefinito.

Grazie per la cura. Ho modificato la domanda mentre guidavi.
— eonil
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.