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.