2013-06-07 28 views
6

Sto lavorando con strutture e ho diverse domande su di loro. Come capisco, le variabili di struttura saranno collocate nella memoria in modo sequenziale. La lunghezza dei blocchi (parole) dipende dall'architettura della macchina (32 bit - 4 byte, 64 bit - 8 byte).C: allineamento strutture dati

consente di dire che abbiamo 2 strutture dati:

struct ST1 { 
    char c1; 
    short s; 
    char c2; 
    double d; 
    int i; 
}; 

In memoria sarà:

32 bit - 20 bytes  
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 
------------------------------------------------------------------------------------------ 
c1| PB| s | s | c1| PB| PB| PB| d | d | d | d | d | d | d | d | i | i | i | i | 

64 bit - 24 bytes | 20 | 21 | 22 | 23 | 
previous sequence + --------------------- 
        | PB | PB | PB | PB | 

Ma possiamo riorganizzare esso, per rendere questa forma di dati in parola della macchina. Come questo:

struct ST2 { 
    double d; 
    int i; 
    short s; 
    char c1; 
    char c2; 
}; 

In questo caso 32 e 64 bit sarà rappresentata allo stesso modo (16 byte):

0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 
---------------------------------------------------------------------- 
d | d | d | d | d | d | d | d | i | i | i | i | s | s | ch1| ch2| 

Ho un paio di domande:

  • È come un'ipotesi, ma la regola principale per struct consiste nel definire le variabili con dimensioni maggiori all'inizio?
  • Come ho capito, non funziona con variabili autonome. Come char str[] = "Hello";?
  • byte di riempimento, che codice ha? È da qualche parte al tavolo ASCII? Spiacente, non ho potuto trovarlo.
  • 2 strutture con tutti i membri rappresentati in memoria da indirizzi diversi e possono essere posizionati non in modo sequenziale in memoria?
  • Tale struttura: struct ST3 { char c1; char c2; char c3;} st3; size = 3, ho capito che se aggiungeremo un membro con un altro tipo, sarà allineato. Ma perché non è allineato prima di esso?

risposta

-1

Fai attenzione, non sei sicuro che le tue variabili siano allineate (ma spesso lo è). Se si utilizza GCC, è possibile utilizzare l'attributo imballato per assicurarsi che i dati siano allineati.

Esempio:

struct foo { 
    char c; 
    int x; 
} __attribute__((packed)); 

A quanto mi risulta non funziona con le variabili indipendenti. Mi piace char str [] = "Ciao" ;?

Questa tabella sarà allineata nella vostra memoria.

+2

wait, what? usando 'packed' si eliminerà il padding, possibilmente forzando i membri a * not * essere allineati correttamente, il che ha implicazioni anche su x86 (ad esempio l'accesso a' double' è atomico solo se è allineato correttamente) – Christoph

+1

Come dice Cristoph, questa risposta è completa disinformazione. – Casey

0

Risposte alle domande poste da (ignorando il vostro molto bella foto della struttura)

E 'come supposizione, ma regola principale per struct è quello di definire variabili con dimensioni più grandi all'inizio?

Inserire sempre le cose che richiedono il maggior allineamento prima. Per esempio non metterei un char[99] prima. In generale questo funziona come puntatori, tipi nativi a 64 bit, tipi nativi a 32 bit, ecc., Ma devi stare molto attento se la tua struttura contiene membri che sono altre strutture.

Come ho capito, non funziona con variabili autonome. Mi piace char str[] = "Hello";

Non lo capisco davvero. Se si definisce un array di caratteri nello stack, questo ha l'allineamento del carattere. Se si definisce un array di caratteri seguito da un int, ci sarà probabilmente un riempimento nello stack, non lo si può trovare.

byte di riempimento, che codice ha? È da qualche parte al tavolo ASCII? Spiacente, non ho potuto trovarlo.

Non ha né codice né dati. È compilato dal compilatore e può contenere qualsiasi valore, che può o non può essere diverso tra le diverse istanze della struttura in esecuzioni uguali o diverse del programma.

2 strutture con tutti i membri rappresentati in memoria da indirizzi diversi e possono essere posizionati non in modo sequenziale in memoria?

Non capisco. Stai chiedendo se il compilatore può inserire il padding tra le strutture? In caso contrario, si prega di chiarire, perché questa risposta non sarà di grande aiuto;

Quando il compilatore crea una struttura, deve consentire di creare in modo intelligente una matrice di tali strutture. Considerate questo:

struct S { 
    int wibble; 
    char wobble; 
}; 

S stuff[2]; 

Se il compilatore non inserisce 3 byte di padding dopo oscillazione, accessi a stuff[1].wobble non sarà allineato correttamente, che si tradurrà in crash su alcuni hardware (e le prestazioni atroce su altro hardware) . Fondamentalmente, il compilatore deve garantire il riempimento alla fine per garantire che il membro più allineato della struttura sia sempre correttamente allineato per una matrice di tali strutture.

Tale struttura: struct ST3 { char c1; char c2; char c3;} st3; Ha dimensione = 3, ho capito che se aggiungeremo un membro con un altro tipo, sarà allineato. Ma perché non è allineato prima di esso?

Intendi "Perché il compilatore non lo inserisce in un punto in cui è correttamente allineato"? Perché la lingua non lo lascia. Il compilatore non è autorizzato a riordinare i membri della tua struttura. È consentito solo inserire padding.

0

L'allineamento del membro di strutture (e classi) dipende dalla piattaforma, vero, ma anche dal compilatore. La ragione di allineare i membri alle sue dimensioni è per motivi di prestazioni. Rendere tutto il tipo integrale allineato con le sue dimensioni riduce l'accesso alla memoria.

In genere è possibile forzare il compilatore a ridurre l'allineamento, ma non è una buona idea se non per motivi specifici (ad esempio, per la compatibilità dei dati tra diverse piattaforme, come dati di comunicazione). In Visual C++ esiste #pragma pack per questo, ad esempio:

#pragma pack(1) 
struct ST1 { 
    char c1; 
    short s; 
    char c2; 
    double d; 
    int i; 
}; 

assert(sizeof(ST1) == 16); 

Ma, come ho detto prima di solito non è una buona idea.

Tenere presente che il compilatore non aggiunge solo i byte del pad dopo alcuni campi. Garantisce inoltre che la struttura sia allocata in memoria perché tutti i campi sono allineati correttamente.Voglio dire, nel campione ST1, perché il tipo di campo più grande è doppia, compilatore sarà assicurato d campo sarà allineato a 8 byte (tranne se si utilizza #pragma pack o simili opzioni):

ST1 st1; 

assert(&st1.d % 8 == 0); 

Circa le vostre domande:

  • Se si desidera risparmiare spazio, sì, è un campo di ordinamento di trucchi per dimensione, scrivendo prima il più grande. Nel caso di strutture composte, usa la dimensione del campo più grande della struttura interna, invece della dimensione della struttura.
  • Sta lavorando su variabili indipendenti. Ma il compilatore può ordinare le variabili in memoria (a differenza di membri di strutture e classi).

Ad esempio:

short s[27]; 
int32_t i32[34]; 
int64_t i64[45]; 

assert(s % 2 == 0); 
assert(i32 % 4 == 0); 
assert(i64 % 8 == 0); 
  • byte di riempimento può contenere qualsiasi cosa. Di solito i dati inizializzati (almeno lo si inizializza). Alcune volte può contenere pattern di byte specifici dal compilatore, per ragioni di debug.
  • Informazioni sulle strutture con tutti i membri rappresentati in memoria da indirizzi diversi: scusate, non capisco bene come chiedete.
  • Il C++ standard dice che l'indirizzo di una struct/classe deve essere lo stesso dell'indirizzo del primo campo di tale struct/class. Quindi, è possibile eseguire il padding solo dopo c3, ma mai prima di c1.

Da N3337 (C++ 11) [9.2 class.menu, p.20]:

Un puntatore a un oggetto struct standard di layout, opportunamente convertito utilizzando un reinterpret_cast, indica il suo membro iniziale (o se quel membro è un campo di bit, quindi all'unità in cui risiede) e viceversa. [ Nota: potrebbe esserci quindi padding senza nome all'interno di un oggetto struct con layout standard , ma non all'inizio, come necessario per ottenere l'allineamento appropriato. Nota -end]

3

Le regole di base sono semplici:

  • membri deve essere lì in ordine (a meno che in C++ si utilizza privato: pubblico: ... sezioni)
  • imbottitura è consentito tra i membri e dopo l'ultimo

Questo è tutto. Il resto è lasciato all'implementazione: lo spazio occupato dai tipi, la quantità di riempimento. Normalmente puoi aspettarti che sia correttamente documentato in ABI o direttamente nel compilatore, e persino avere strumenti per la manipolazione.

In pratica imbottitura è necessario in alcune architetture, dire SPARC richiede 32 bit "int" ricalca l'indirizzo divisibile per 4. In altri non è requisito ma entità disallineati possono richiedere più tempo per elaborare, dire una Il processore 80286 richiede un ciclo aggiuntivo per leggere l'entità a 16 bit da un indirizzo dispari. (Prima di dimenticare: la rappresentazione dei tipi è diversa!)

È normale che il requisito di allineamento o le prestazioni migliori corrispondano esattamente: si deve allineare al contorno come le dimensioni. Un buon esempio contrario è il numeri 80 bit floating point (disponibile come doppia doppia o lunga in alcuni compilatori) che hanno preferito allineamento 8 o 16 byte anziché 10.

Per violino con compilatore imbottitura solitamente invia un passa a predefinito. Ciò cambia da versione a versione, quindi è meglio tener conto dell'upgrade. E all'interno della funzione di sostituzione del codice come _attribute__(packed) in gcc e #pragma pacchetto in MS e molti altri. Queste sono tutte estensioni dello standard ovviamente.

La linea di fondo è, se si vuole giocare con il layout, si inizia a leggere il dox di tutti i compilatori per i quali si desidera, ora e in futuro, sapere cosa fanno e come controllarlo. Forse anche leggere dox delle piattaforme di destinazione, a seconda del motivo per cui sei interessato al layout in primo luogo.

Una motivazione abituale è quella di avere un layout stabile mentre si scrive la memoria grezza su file e si aspetta di leggerlo di nuovo. Forse su una piattaforma diversa usando un compilatore diverso. Questo è più facile finché un nuovo tipo di piattaforma non entra nella scena.

Un'altra motivazione è la prestazione. Quella è molto più complicata, poiché le regole cambiano velocemente e l'effetto è difficile da prevedere immediatamente. Dite su Intel che la penalità di base "disallineata" è andata via per molto tempo, invece ciò che conta è essere all'interno di una linea di cache. Dove la dimensione della linea della cache varia in base al processore. Anche l'utilizzo di più padding può produrre un individuo migliore mentre le strutture completamente impacchettate sono più economiche nell'utilizzo della cache.

E alcune operazioni richiedono un allineamento corretto, ma non sono applicate direttamente dal compilatore, potrebbe essere necessario applicare speciali pragmi di allineamento (come per alcune cose correlate a SSE).

Riga inferiore ripetuta: smetti di indovinare, decidi i tuoi obiettivi e leggi il dox giusto. (Btw per me la lettura dei manuali di architettura per SPARC, IA32 e altri era tremendamente divertente e di guadagno per molti aspetti.)

0

di gcc su architettura Intel sua prende più istruzioni e cicli di accesso (lettura/scrittura) indirizzo di memoria dispari numerati. così il padding è stato aggiunto per raggiungere l'indirizzo di memoria di numero pari