Esiste un modo standard o un'alternativa standard al confezionamento di una struttura in c?


13

Quando la programmazione in CI ha trovato inestimabile impacchettare le strutture usando l' __attribute__((__packed__))attributo GCCs in modo da poter facilmente convertire una porzione strutturata di memoria volatile in una matrice di byte da trasmettere su un bus, salvata in memoria o applicata a un blocco di registri. Le strutture impacchettate garantiscono che, se trattate come una matrice di byte, non conterranno alcuna imbottitura, che è allo stesso tempo dispendiosa, un possibile rischio per la sicurezza e possibilmente incompatibile quando si interfaccia con l'hardware.

Non esiste uno standard per l'imballaggio delle strutture che funziona in tutti i compilatori C? In caso contrario, sono un outlier nel pensare che questa sia una caratteristica fondamentale per la programmazione dei sistemi? I primi utenti del linguaggio C non hanno trovato necessità di impacchettare le strutture o esiste qualche tipo di alternativa?


usare le strutture attraverso domini di compilazione è una pessima idea, in particolare puntare all'hardware (che è un altro dominio di compilazione). Le strutture di pacchetti sono solo un trucco per farlo, hanno molti effetti collaterali negativi, quindi ci sono molte altre soluzioni ai tuoi problemi con meno effetti collaterali e che sono più portatili.
old_timer

Risposte:


12

In una struttura, ciò che conta è l'offset di ciascun membro dall'indirizzo di ciascuna istanza di struttura. Non è tanto la questione di quanto siano strette le cose.

Un array, tuttavia, conta nel modo in cui è "impacchettato". La regola in C è che ogni elemento dell'array è esattamente N byte dal precedente, dove N è il numero di byte utilizzati per memorizzare quel tipo.

Ma con una struttura, non esiste un tale bisogno di uniformità.

Ecco un esempio di uno strano schema di imballaggio:

Freescale (che produce microcontrollori per il settore automobilistico) crea un micro con un coprocessore di unità di elaborazione del tempo (google per eTPU o TPU). Ha due dimensioni di dati nativi, 8 bit e 24 bit e si occupa solo di numeri interi.

Questa struttura:

struct a
{
  U24 elementA;
  U24 elementB;
};

vedrà ogni U24 memorizzato il proprio blocco a 32 bit, ma solo nell'area di indirizzo più alta.

Questo:

struct b
{
  U24 elementA;
  U24 elementB;
  U8  elementC;
};

avrà due U24s memorizzato in blocchi adiacenti 32 bit, e la U8 sarà memorizzato nel "buco" davanti al primo U24, elementA.

Ma puoi dire al compilatore di mettere tutto nel suo blocco a 32 bit, se vuoi; è più costoso su RAM ma utilizza meno istruzioni per gli accessi.

"impacchettare" non significa "impacchettare strettamente" - significa solo uno schema per disporre gli elementi di una struttura rispetto all'offset.

Non esiste uno schema generico, dipende dal compilatore + dall'architettura.


1
Se il compilatore per le riordina TPU struct bper spostare elementCprima di tutti gli altri elementi, allora non è un compilatore conforme C. La riorganizzazione degli elementi non è consentita in C
Bart van Ingen Schenau,

Interessante ma U24 non è un tipo C standard en.m.wikipedia.org/wiki/C_data_types, quindi non sorprende che il complier sia costretto a gestirlo in un modo un po 'strano.
satur9nine

Condivide la RAM con il core della CPU principale che ha una dimensione della parola di 32 bit. Ma questo processore ha un ALU che si occupa solo di 24 bit o 8 bit. Quindi ha uno schema per disporre numeri a 24 bit in parole a 32 bit. Non standard, ma un ottimo esempio di imballaggio e allineamento. D'accordo, è molto non standard.
RichColours

6

Quando la programmazione in CI ha trovato inestimabile impacchettare le strutture usando GCC __attribute__((__packed__))[...]

Dato che dici __attribute__((__packed__)), presumo che la tua intenzione sia quella di eliminare tutto il padding all'interno di un struct(fare in modo che ogni membro abbia un allineamento di 1 byte).

Non esiste uno standard per l'imballaggio delle strutture che funziona in tutti i compilatori C?

... e la risposta è "no". Il riempimento e l'allineamento dei dati relativi a una struttura (e matrici contigue di strutture in pila o heap) esistono per un motivo importante. Su molte macchine, l'accesso alla memoria non allineato può comportare una riduzione delle prestazioni potenzialmente significativa (sebbene si riduca su un hardware più recente). In alcuni casi rari, l'accesso alla memoria disallineato porta a un errore del bus irrecuperabile (può persino causare l'arresto anomalo dell'intero sistema operativo).

Poiché lo standard C è incentrato sulla portabilità, non ha molto senso avere un modo standard per eliminare tutta l'imbottitura in una struttura e consentire solo che i campi arbitrari vengano disallineati, dal momento che ciò rischierebbe potenzialmente di rendere il codice C non portabile.

Il modo più sicuro e portatile per trasmettere tali dati a una fonte esterna in modo da eliminare tutto il riempimento è serializzare da / a flussi di byte invece di cercare di inviare il contenuto della memoria non elaborata structs. Ciò impedisce anche al tuo programma di subire penali di prestazione al di fuori di questo contesto di serializzazione e ti consentirà inoltre di aggiungere liberamente nuovi campi a structsenza buttare via e glitch l'intero software. Ti darà anche un po 'di spazio per affrontare l'endianità e cose del genere se mai dovesse diventare una preoccupazione.

Esiste un modo per eliminare tutto il riempimento senza raggiungere le direttive specifiche del compilatore, sebbene sia applicabile solo se l'ordine relativo tra i campi non ha importanza. Dato qualcosa del genere:

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 rispetto all'indirizzo della struttura contenente questi campi, in questo modo:

0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF
x_______y.......x_______y.......x_______y.......x_______y.......

... dove .indica imbottitura. Ciascuno xdeve allinearsi a un limite di 8 byte per le prestazioni (e talvolta anche il comportamento corretto).

Puoi eliminare il riempimento in modo portatile usando una rappresentazione SoA (struttura dell'array) in questo modo (supponiamo di aver bisogno di 8 Fooistanze):

struct Foos
{
   double x[8];
   char y[8];
};

Abbiamo demolito efficacemente 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ù il sovraccarico di padding e senza comportare un accesso disallineato alla memoria poiché non stiamo più accedendo a questi campi di dati come offset di un indirizzo di struttura, ma invece 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 sia del minor consumo di dati (nessuna imbottitura più irrilevante nel mix per rallentare la velocità di consumo dei dati rilevanti della macchina) sia di un potenziale per il compilatore di vettorializzare l'elaborazione in modo molto banale .

Il rovescio della medaglia è che è un codice PITA da codificare. È anche potenzialmente meno efficiente per l'accesso casuale con il passo più grande tra i campi, dove spesso le ripetizioni AoS o AoSoA faranno meglio. Ma questo è un modo standard per eliminare l'imbottitura e imballare le cose nel modo più stretto possibile senza rovinare l'allineamento di tutto.


2
Direi che avere un mezzo per specificare esplicitamente il layout della struttura migliorerebbe enormemente la portabilità. Mentre alcuni layout porterebbero a codice molto efficiente su alcune macchine e codice molto inefficiente su altre, il codice funzionerebbe su tutte le macchine e sarebbe efficace su almeno alcune. Al contrario, in assenza di tale funzionalità, l'unico modo per far funzionare il codice su tutte le macchine è probabilmente quello di renderlo inefficiente su tutte le macchine oppure utilizzare un sacco di macro e compilazione condizionale per combinare un veloce non portatile programma e un portatile lento nella stessa fonte.
supercat,

Concettualmente sì, se potessimo specificare tutto in bit e byte rappresentazioni, requisiti di allineamento, endianness, ecc. E avere una caratteristica che consente tale controllo esplicito in C mentre opzionalmente lo separiamo ulteriormente dall'architettura sottostante ... Ma stavo solo parlando di ATM: attualmente la soluzione più portatile per un serializzatore è di scriverlo in modo tale che non dipenda dalle rappresentazioni esatte di bit e byte e dall'allineamento dei tipi di dati. Purtroppo non disponiamo dei mezzi ATM per fare altrimenti in modo efficace (in C).

5

Non tutte le architetture sono uguali, basta attivare l'opzione a 32 bit su un modulo e vedere cosa succede quando si utilizza lo stesso codice sorgente e lo stesso compilatore. L'ordine dei byte è un'altra limitazione ben nota. Gettare in rappresentazione in virgola mobile e i problemi peggiorano. L'uso dell'imballaggio per inviare dati binari non è portatile. Per standardizzarlo in modo che fosse praticamente utilizzabile, è necessario ridefinire le specifiche del linguaggio C.

Sebbene sia comune, utilizzare Pack per inviare dati binari è una cattiva idea se si desidera sicurezza, portabilità o longevità dei dati. Con quale frequenza leggi un BLOB binario da una sorgente nel tuo programma. Quanto spesso controlli che tutti i valori siano sani, che un hacker o un cambio di programma non abbia "ottenuto" i dati? Quando hai codificato una routine di controllo, potresti anche utilizzare le routine di importazione ed esportazione.


0

Un'alternativa molto comune è "padding denominato":

struct s {
  short s1;
  char  c2;
  char  reserved; // Padding
};

Questo fa assumere la struttura non viene arrotondato per 8 byte.

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.