compact Effetti di__attribute__((impacchettato)) sull'array nidificato di strutture?



struct align c (1)

Il problema

Sto lavorando per inviare una struttura grezza su una rete a un programma noto dall'altra parte, ma devo preoccuparmi della memoria introdotta silenziosamente usata per allineare le strutture (sono trattati altri aspetti come l'endianità). Quello con cui sto lavorando è qualcosa del tipo:

typedef struct __attribute__((packed))
{
   uint16_t field1;
   uint16_t field2;
   uint16_t field3;
} packed_1_s;

typedef struct __attribute__((packed))
{
   uint16_t fieldA;
   uint16_t fieldB;
   packed_1_s structArray[10];
} packed_2_s;

typedef struct __attribute__((packed))
{
   uint16_t fieldX;
   packed_2_s fieldY;
   uint8_t arrayZ[20];
} data_s;

Capisco che normalmente la struttura packed_1_s potrebbe / avrebbe spazio aggiuntivo assegnato per ogni istanza della struttura per riempirlo con le dimensioni preferite del compilatore (dipende dall'hardware per cui è stato costruito), e che le dimensioni preferite possono essere ovunque da 2 byte a 64 byte (più di recente). Normalmente se avessi una singola istanza di packed_1_s in packed_2_s non ci sarebbero problemi, ma mi viene dato di capire che ci sono alcune differenze quando provi a mettere gli elementi in un array.

Tentativo di soluzioni

La documentazione di gcc sembra suggerire che semplicemente includendo l'attributo packed nella definizione packed_2_s, i campi, anche se sono array, saranno tutti il ​​più fitti possibile e non aggiungeranno spazio alla struttura packed_2_s per allineare gli elementi dell'array. Tuttavia, la documentazione sull'attributo align () suggerisce che gli array sono gestiti in modo diverso rispetto ad altri campi e hanno bisogno che l'attributo align / packed sia impostato direttamente sul campo per modificare la spaziatura aggiuntiva aggiunta per corrispondere all'allineamento specificato (o alla sua mancanza). Ho provato a impostare l'attributo packed su entrambi i campi structArray, e quando ciò non ha funzionato, ho fatto un test impostando l'attributo packed su arrayZ nel codice qui sopra:

packed_1_s structArray[10] __attribute__((packed));

uint8_t arrayZ[20] __attribute__((packed));

Entrambi i tentativi mi hanno dato un avvertimento del compilatore che l'attributo packed non era compreso in questo contesto e sarebbe stato saltato (buona cosa che ho creato con "-Wall").

Speravo che un modo per aggirare il problema sarebbe stato utilizzare l'attributo align (1), che indica un allineamento desiderato di 1 byte che è paragonabile all'attributo packed, ma la documentazione dice che l'attributo align () può solo aumentare l'allineamento e dovrebbe essere utilizzato per diminuire l'allineamento.

considerazioni

Da quello che posso determinare dalla documentazione GCC, sembra che ci siano 3 casi principali di inserimento di memoria aggiuntiva.

  1. I contenuti della struttura possono avere memoria aggiuntiva allocata alla struttura stessa per modificare la spaziatura tra i campi.
    In effetti, la definizione della mappa di memoria dei contenuti della struttura all'interno della struttura può cambiare (sebbene non l'ordine degli elementi).
  2. Le strutture possono avere memoria aggiuntiva assegnata a loro per riempirle in una dimensione complessiva più efficiente. Questo è generalmente inteso in modo che altre variabili che vengono dopo una dichiarazione di una delle loro istanze non rientrino nello stesso "blocco" come l'istanza della struttura in cui un "blocco" è definito dal sistema / compilatore.
  3. Gli array, all'interno o meno di una struttura, possono avere una memoria aggiuntiva aggiunta per spostare gli elementi in un allineamento efficiente.

Per quanto posso dire, l'attributo packed può essere usato per influenzare le strutture e bloccare la memoria aggiuntiva aggiunta nel caso 1 e 2 sopra, ma non sembra essere un modo per gestire il caso 3 sopra sul mio compilatore ).

La domanda

Esiste un modo per garantire che la struttura data_s non abbia assolutamente alcuno spazio aggiuntivo aggiunto a essa o a nessuna delle sue sottostrutture, quindi non ho spostamenti del compilatore nella mappa della memoria? Sto fraintendendo i casi in cui il compilatore può inserire lo spazio per spostare intenzionalmente la mappa della memoria?

MODIFICARE

Ho discusso alcuni dei problemi con il mio guru locale e sembra che io abbia qualche incomprensione del caso 3 sopra. Gli elementi nella matrice non hanno spazio inserito tra di loro, ma lo spazio aggiuntivo per garantire che si allineano correttamente viene aggiunto alla struttura stessa. Apparentemente questo suggerisce cose come "sizeof (structureOnlyContaining_uint32_t)" non restituirà sempre "4" poiché potrebbe essere aggiunto ulteriore spazio per allineare il tipo di dati uint32_t sul compilatore utilizzato. Il risultato è che ci sono davvero solo 2 casi:

  1. Offset più grandi tra i campi nella mappa della memoria della struttura.
    Lo spazio tra i campi può essere modificato per allineare ogni campo. Questo può essere modificato usando gli attributi packed o align ().
  2. Imbottitura della struttura. La dimensione di una struttura, restituita da sizeof (), può essere modificata in modo che le matrici delle strutture finiscano correttamente allineate per il sistema. Ciò consente a tutti i sistemi di presumere che l'inizio delle strutture sarà sempre allineato, causando problemi se non lo sono. Questo non sembra influenzato dagli attributi pack o align.

A causa del nuovo caso 2, gli elementi di un array in una struttura non obbediscono necessariamente agli attributi packed o align () specificati nella struttura, sebbene l'inizio dell'array e il campo immediatamente successivo all'array.

La mia domanda è quindi su come trattare con structArray in packed_2_s poiché la dimensione dell'array nel suo complesso non può essere garantita esclusivamente dall'attributo packed. C'è un modo per garantire la dimensione fissa del campo structArray nel suo complesso? Va notato che non posso aumentare troppo le dimensioni di packed_1_s dato che la struttura data_s deve essere mantenuta il più piccola possibile (sostituendo i dati audio / video in uno scenario di streaming).


Nota i seguenti punti su __attribute__((packed)) :

  • Quando packed viene usato in una dichiarazione di struttura, comprimerà i suoi campi come, ad esempio, sizeof (struttura) == sizeof (first_member) + ... + sizeof (last_member).

  • Qui, un array è solo un membro della struttura. L'imballaggio della struttura di contenimento di una matrice non cambierà la dimensione dell'array. In effetti, la dimensione di (qualsiasi) matrice è sempre sizeof (elemento) * numero_di_elemento.

  • Allo stesso modo, l'imballaggio di una struttura di contenimento di una struttura interna non cambierà la dimensione della struttura interna. La dimensione di una struttura è completamente determinata dalla sua dichiarazione ed è la stessa indipendentemente da dove la usi.

  • L'imballaggio di una struttura renderà necessario l'allineamento di un byte (cioè può essere posizionato ovunque nella memoria).

  • L'imballaggio introdurrà problemi di allineamento quando si accede ai campi di una struttura compressa. Il compilatore terrà conto di ciò quando i campi sono accessibili direttamente, ma non quando sono accessibili tramite puntatori . Naturalmente, questo non si applica ai campi con l'allineamento richiesto uno (come char o altre strutture impacchettate). Vedi la mia risposta a una domanda simile , che include un programma che dimostra il problema con l'accesso ai membri tramite i puntatori.

Infine, per rispondere alla domanda,

Esiste un modo per garantire che la struttura data_s non abbia assolutamente alcuno spazio aggiuntivo aggiunto a essa o a nessuna delle sue sottostrutture, quindi non ho spostamenti del compilatore nella mappa della memoria?

Sì. Dichiarare la struttura come impacchettata e anche tutte le strutture che essa contiene, in modo ricorsivo.

Si noti inoltre che l'attributo compresso si applica a una dichiarazione di struttura e non a un tipo. Non esiste una versione compressa di una struttura dichiarata non imballata. Quando si utilizza una struttura da qualche parte, essa (i suoi membri) sarà imballata se e solo se la struttura stessa è stata dichiarata imballata. Questo è in qualche modo implicito dal fatto che la dimensione di una struttura è completamente determinata dalla sua dichiarazione.

AGGIORNAMENTO : per qualche motivo sei ancora confuso sugli array. La soluzione che ho fornito (dichiara tutte le strutture impacchettate) funziona anche con gli array . Per esempio:

struct elem_struct {
    uint32_t x;
} __attribute__((packed));
// packed guarantees that sizeof(struct elem_struct) = sizeof(uint32_t) = 4

struct array_struct {
    struct elem_struct arr[10];
} __attribute__((packed));
// packed guarantees that sizeof(struct array_struct) =
// = sizeof(struct elem_struct[10]) = 10 * sizeof(struct elem_struct)
// = 10 * 4 = 40

I due punti aggiuntivi che hai fatto sugli array sono veri, ma solo quando le strutture non sono piene. L'impacchettamento costringe i campi della struct ad essere continui, e ciò crea problemi di allineamento che, se non fosse usato alcun packing, sarebbero risolti inserendo uno spazio vuoto tra i membri e riempendo la struct (si veda il punto che ho già sollevato sull'allineamento).





struct