2015-04-01 26 views
8

vedi esempio in linea: Ideone examplePerché la dimensione della struttura 5 compresso invece di 4 byte qui?

struct { 
    union { 
    struct { 
     uint32_t messageID : 26; 
     uint8_t priority : 3; 
    } __attribute__ ((packed)); 
    uint32_t rawID : 29; 
    } __attribute__ ((packed)); 
    uint8_t canFlags : 3; 
} __attribute__ ((packed)) idSpecial; 

Perché il rapporto compilatore la dimensione della struttura di 5 byte invece di 4 qui? Dovrebbe contenere 32 bit.

+0

possibile duplicato di [Strutture e unioni in C, determinazione delle dimensioni e accesso ai membri] (http://stackoverflow.com/questions/3380118/structures-and-unions-in-c -determining-size-and-accessing-members) –

+0

@DavidTitarenco: Non penso che quel particolare post sia valido per i bitfield. Non mi sorprenderebbe se questa risposta fosse stata da qualche altra parte. –

+4

I risponditori devono notare che l'OP sta utilizzando l'estensione GCC non standard __attribute__ ((compressa))] (https://gcc.gnu.org/onlinedocs/gcc/Type-Attributes.html) per evitare il riempimento . Quindi, dire che il compilatore può inserire padding in ogni caso non è corretto - quando viene usato '__attribute __ ((packed))', GCC organizzerà la struttura per evitare il padding (rendendo l'accesso più costoso su piattaforme che non supportano letture non allineate). – user4815162342

risposta

5

A causa dell'allineamento della memoria: il compilatore non verrà avviato canFlags nel medio di un byte, verrà avviato all'inizio del byte successivo (probabilmente *). Quindi hai quattro byte per l'unione iniziale e un byte per canFlags.

Se, per esempio, si è spostato canFlags nell'unione, sarebbe (forse *) hanno formato 4:

typedef struct structTag { 
    union { 
    struct { 
     uint32_t messageID : 26; /* 26bit message id, 67108864 ids */ 
     uint8_t priority : 3; /* priority: MUST BE 0 */ 
    } __attribute__ ((packed)); 
    uint32_t rawID : 29; 
    uint8_t canFlags : 3; /* <==== Moved */ 
    } __attribute__ ((packed)); 
} __attribute__ ((packed)) idSpecial; 

Updated example on ideone. Ovviamente, quel cambiamento specifico probabilmente non è quello che vuoi; Sto solo dimostrando che il problema sta avviando un nuovo campo non su un limite di byte.


* "probabilmente" perché alla fine spetta al compilatore.

+0

canFlags accanto a rawid li rende union, il che non è ciò che l'OP voleva, penso. –

+0

@MatsPetersson: Giusto, gli OP dovranno ridisegnare, stavo solo sottolineando perché stanno ottenendo 5 invece di 4 per il loro design attuale. –

+1

Usando il tuo suggerimento, abbiamo spostato 'rawID' e' canFlags' in una struttura e aggiunto dummy padding di 3 bit alla fine della prima struttura all'interno dell'unione e ora l'intera struttura è di 4 byte grande come previsto. –

0

In alcuni compilatori, per "unire" i bit, tutti gli elementi devono essere dello stesso tipo. Così lo rendono uint32_t dove ora avete uint8_t - questo sembra non essere il caso nella Ideone compilatore utilizza tho'

[Non importa cosa, E' ancora fino al compilatore come si fonde i bit, quindi è l'unica il modo per garantire assolutamente che i tuoi dati siano memorizzati come 32 bit è usare un singolo uint32_t e dichiarare una classe che fa il relativo shifting e anding/oring per manipolare il valore - l'unica garanzia che hai è che UN elemento nella tua struct avrà almeno tanti bit quanti ne hai richiesti]

Come altri hanno sottolineato, non è possibile avviare una nuova struttura su un limite diverso da un byte. Ho riparato avendo un secondo struct all'interno dell'unione, in questo modo: http://ideone.com/Mr1gjD

#include <stdint.h> 
#include <stdio.h> 

typedef struct structTag { 
    union { 
    struct { 
     uint32_t messageID : 26; /* 26bit message id, 67108864 ids */ 
     uint8_t priority : 3; /* priority: MUST BE 0 */ 
    } __attribute__ ((packed)); 
    struct { 
     uint32_t rawID : 29; 
     uint8_t canFlags : 3; 
    }; 
    } __attribute__ ((packed)); 
} __attribute__ ((packed)) idSpecial; 

int main() { 
    printf("size: %d", sizeof(idSpecial)); 
    return 0; 
} 
+0

@ T.J.Crowder: concordato. Anche se ho visto casi in cui ciò è importante per qualche ragione - penso che potrebbero essere i compilatori di MS che hanno quel problema. –

6

Il problema è che __attribute__((packed)) non esegue imballaggio bit. Garantisce solo che non ci sono imbottiture tra i membri struct. Si può provare questo esempio più semplice, dove la dimensione è segnalato anche come 5:

typedef struct structTag { 
    struct { 
     uint32_t messageID : 26; 
     uint8_t priority : 3; 
    } __attribute__ ((packed)); 
    uint8_t canFlags : 3; 
} __attribute__ ((packed)) idSpecial; 

Bitwise imballaggio è possibile solo per i membri BITFIELD. Dovrai riprogettare la tua struct per essere un'unione di una struct con bitfield messageID/priority/canFlags, e una struct con bitfields rowID/canFlags. In altre parole, sarà necessario avere qualche duplicazione o ricorrere alle macro accessorie o alle funzioni membro.

1

I dati sono disposti e accessibili nella memoria del computer mediante l'allineamento della struttura dei dati. Che ha due questioni correlate

  1. allineamento
  2. Imbottitura

Quando un'operazione di scrittura viene eseguita da computer, scrive di solito in multiplo di 4 byte (per i sistemi a 32 bit). Una ragione per questo atto è l'obiettivo di aumentare le prestazioni.Pertanto, quando si scrive una struttura dati, che ha prima una variabile da 1 byte e quindi una variabile da 4 byte, eseguirà il riempimento dopo i primi 1 byte di dati per allinearla su limiti a 32 bit.

struct { 
    union { 
    struct { 
     uint32_t messageID : 26; 
     uint8_t priority : 3; 
    } __attribute__ ((packed)); 
    uint32_t rawID : 29; 
    } __attribute__ ((packed)); 
    uint8_t canFlags : 3; 
} __attribute__ ((packed)) idSpecial; 

Ora nella struttura dati sopra riportati si utilizza __attribute__ ((packed)) che significa senza imbottitura. Quindi uint32_t è di 4 byte, ma si sa che ha 26 bit e 3 bit per priorità. Ora, poiché avete entrambe le variabili in una struttura, riserverà 32 bit anziché 29 in modo che le informazioni della prima struttura siano allineate sui limiti.

Ora per canFlags Avrà bisogno di altri byte. Quindi questo rende 5 byte anziché 4.