2012-07-01 23 views
9

Lavorare in C11, il seguente struct:Un errore in attuazione GCC di bit-field

struct S { 
    unsigned a : 4; 
    _Bool b : 1; 
}; 

ottiene spiegate da GCC come un unsigned (4 byte) di cui vengono utilizzati 4 bit, seguito da un _Bool (4 byte) di cui viene utilizzato 1 bit, per una dimensione totale di 8 byte.

Si noti che C99 e C11 consentono in particolare _Bool come membro del campo di bit. Lo standard C11 (e probabilmente anche C99) afferma anche sotto §6.7.2.1 'Struttura e unione prescrittori' ¶11 che:

L'attuazione può allocare qualsiasi unità di memorizzazione indirizzabile abbastanza grande da contenere un po 'campo. Se rimane abbastanza spazio, un campo di bit che segue immediatamente un altro campo di bit in una struttura deve essere impacchettato in bit adiacenti della stessa unità.

Quindi ritengo che l'organo b sopra avrebbe dovuto essere confezionato nel contenitore allocata per il membro a, risultando in una struttura di dimensione totale di 4 byte.

GCC comporta correttamente e l'imballaggio si verifica quando si utilizzano gli stessi tipi per i due membri, o quando si è unsigned e l'altra signed, ma i tipi unsigned e _Bool sembrano essere considerate troppo distinta da GCC perché gestirli correttamente.

Qualcuno può confermare la mia interpretazione dello standard e che questo è davvero un bug GCC?

Sono anche interessato a un work-around (alcuni switch del compilatore, pragma, __attribute__ ...).

sto usando gcc 4.7.0 con -std=c11 (anche se altre impostazioni mostrano lo stesso comportamento.)

+0

Si noti che l'estensione GCC '__attribute__ ((packed))' può essere applicata ai membri qui , ma è ortogonale a questo problema (risulta in una struttura di dimensione 4 + 1 = 5, vale a dire con lo stesso problema). – ndkrempel

+0

Correlati: http://stackoverflow.com/questions/308364/c-bitfield-packing-with -bools (ma si riferisce a C++, che non è altrettanto impegnativo nella sua formulazione sui campi di bit). – ndkrempel

+0

In base a una risposta alla domanda collegata sopra, questo comportamento non si è verificato in gcc 4.2.4, quindi potrebbe essere una regressione da allora. – ndkrempel

risposta

10

Il problema descritto è compatibile con gli standard C99 e C11, ma è previsto per la compatibilità binaria con il compilatore MSVC (che ha un comportamento imballaggio struct inusuale.)

Fortunatamente, può essere disabilitata sia nel codice con __attribute__((gcc_struct)) applicato alla struct, o con l'opzione di riga di comando -mno-ms-bitfields (vedere la documentation).

+0

È documentato ovunque? Non riesco a trovare nulla di utile su 'mno-ms-bitfields' –

+0

https://gcc.gnu.org/onlinedocs/gcc/x86-Options.html#x86-Options – ndkrempel

+0

Grazie, non so perché potrei non lo trovi prima. Sarebbe utile modificarlo nella risposta in quanto i commenti non sono pensati per essere a lungo termine. –

0

Utilizzando sia GCC 4.7.1 (in casa costruita) e GCC 4.2.1 (LLVM/clang †) su Mac OS X 10.7.4 con una compilazione a 64 bit, i rendimenti questo codice in modalità 4-std=c99:

#include <stdio.h> 

int main(void) 
{ 
    struct S 
    { 
     unsigned a : 4; 
     _Bool b : 1; 
    }; 
    printf("%zu\n", sizeof(struct S)); 
    return 0; 
} 

Questo è la metà delle dimensioni che stai segnalando su Windows. Mi sembra sorprendentemente grande (mi aspetto che sia una dimensione di 1 byte), ma le regole della piattaforma sono quelle che sono. Fondamentalmente, il compilatore non è obbligato a seguire le regole che desideri; può seguire le regole della piattaforma su cui viene eseguito, e dove ha la possibilità, può anche definire le regole della piattaforma su cui viene eseguito.

Questo seguente programma ha un comportamento leggermente dubbia (perché accede u.i dopo u.s ultimo scritto), ma mostra che il campo a viene memorizzato nei 4 bit meno significativi e il campo b viene memorizzato nel bit successivo:

#include <stdio.h> 

int main(void) 
{ 
    union 
    { 
     struct S 
     { 
      unsigned a : 4; 
      _Bool b : 1; 
     } s; 
     int i; 
    } u; 
    u.i = 0; 
    u.s.a = 5; 
    u.s.b = 1; 
    printf("%zu\n", sizeof(struct S)); 
    printf("%zu\n", sizeof(u)); 
    printf("0x%08X\n", u.i); 
    u.s.a = 0xC; 
    u.s.b = 1; 
    printf("0x%08X\n", u.i); 
    return 0; 
} 

uscita:

4 
4 
0x00000015 
0x0000001C 

† i686-apple-darwin11-LLV m-gcc-4.2 (GCC) 4.2.1 (Basato su Apple Inc. build 5658) (LLVM build 2336.9.00)