When programming in C I have found it invaluable to pack structs using
GCCs __attribute__((__packed__))
[...]
Dato che menzioni __attribute__((__packed__))
, presumo che la tua intenzione sia quella di eliminare tutto il padding entro un struct
(fai in modo che ogni membro abbia un allineamento di 1 byte).
Is there no standard for packing structs that works in all C
compilers?
... e la risposta è "no". Il riempimento e l'allineamento dei dati relativi a una struttura (e agli array contigui di strutture nello stack o heap) esistono per una ragione importante. Su molte macchine, l'accesso alla memoria non allineato può portare a una penalità prestazionale potenzialmente significativa (sebbene diventi meno su un hardware più recente). In alcuni scenari rari, l'accesso alla memoria disallineato porta a un errore del bus irrecuperabile (potrebbe persino danneggiare l'intero sistema operativo).
Dato che lo standard C è incentrato sulla portabilità, non ha senso avere un modo standard per eliminare tutto il padding in una struttura e consentire semplicemente che i campi arbitrari siano disallineati, poiché farlo rischia potenzialmente di rendere il codice C non-portatile .
Il modo più sicuro e più portabile per inviare tali dati a una fonte esterna in modo da eliminare tutto il padding è serializzare su / da flussi di byte invece di provare semplicemente a inviare il contenuto della memoria grezza del structs
. Ciò impedisce anche al tuo programma di subire penalizzazioni prestazionali al di fuori di questo contesto di serializzazione e ti permetterà anche di aggiungere liberamente nuovi campi a una struct
senza buttare via e glitching dell'intero software. Ti darà anche spazio per affrontare endianness e cose del genere se mai dovesse diventare un problema.
C'è un modo per eliminare tutto il padding senza raggiungere le direttive specifiche del compilatore, sebbene sia applicabile solo se l'ordine relativo tra i campi non ha importanza. Dato qualcosa di simile:
struct Foo
{
double x; // assume 8-byte alignment
char y; // assume 1-byte alignment
// 7 bytes of padding for first field
};
... abbiamo bisogno del padding per l'accesso alla memoria allineato relativo all'indirizzo della struttura che contiene questi campi, in questo modo:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......
... dove .
indica padding. Ogni x
deve essere allineato a un limite di 8 byte per le prestazioni (e talvolta anche un comportamento corretto).
Puoi eliminare il padding in modo portatile usando una rappresentazione SoA (structure of array) in questo modo (supponiamo di aver bisogno dell'8% di istanze diFoo
):
struct Foos
{
double x[8];
char y[8];
};
Abbiamo effettivamente demolito la struttura. In questo caso, la rappresentazione della memoria diventa così:
0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______x_______x_______x_______x_______x_______x_______x_______
... e questo:
01234567
yyyyyyyy
... non più sovraccarico di padding e senza coinvolgere l'accesso alla memoria non allineato poiché non stiamo più accedendo a questi campi dati come offset di un indirizzo di struttura, ma come offset di un indirizzo di base per ciò che effettivamente è un array .
Ciò comporta anche il vantaggio di essere più veloce per l'accesso sequenziale a causa della minore quantità di dati da consumare (non più irrilevante riempimento nel mix per rallentare la velocità di consumo dei dati della macchina) e anche del potenziale per il vettore di vettorializzare elaborazione molto banale.
Il rovescio della medaglia è che è un PITA da codificare. È anche potenzialmente meno efficiente per l'accesso casuale con il passo più lungo tra i campi, dove spesso i rappresentanti AoS o AoSoA fanno meglio. Ma questo è un modo standard per eliminare l'imbottitura e imballare le cose il più strettamente possibile senza rovinare l'allineamento di tutto.