2012-07-01 15 views
9

Arbeiten in C11, die folgende Struktur:Ein Fehler in GCC Implementierung von Bitfeldern

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

wird von GCC als ein unsigned (4 Bytes), davon 4 Bits werden verwendet, gefolgt von einem _Bool layed out (4 Bytes), von denen 1 Bit verwendet wird, für eine Gesamtgröße von 8 Bytes.

Beachten Sie, dass C99 und C11 speziell _Bool als Bitfeldelement zulassen. Der C11-Standard (und wahrscheinlich C99 auch) heißt es auch unter §6.7.2.1 ‚Struktur und Vereinigung Bezeich‘ ¶11 dass:

Eine Implementierung jede adressierbare Speichereinheit zuweisen kann groß genug, um ein Bit-Feld zu halten. Wenn genügend Speicherplatz vorhanden ist, sollte ein Bitfeld, das unmittelbar einem anderen Bitfeld in einer Struktur folgt, in benachbarte Bits derselben Einheit gepackt werden.

Deshalb glaube ich, dass das Mitglied b oben in die Speichereinheit für das Element a zugeordnet verpackt worden sein, in einer Struktur von Gesamtgröße 4 Byte ergibt.

GCC verhält sich korrekt und Verpackung auftritt, wenn die gleichen Typen für die beiden Mitglieder, oder wenn man unsigned und die andere signed, aber die Typen unsigned und _Bool scheinen zu verschieden zu betrachten, von GCC für sie mit ihnen umgehen korrekt.

Kann jemand meine Interpretation des Standards bestätigen, und das ist in der Tat ein GCC-Fehler?

Ich bin auch interessiert an einem Workaround (einige Compiler-Schalter, Pragma, __attribute__ ...).

Ich bin mit gcc 4.7.0 mit -std=c11 (obwohl anderen Einstellungen das gleiche Verhalten zeigen.)

+0

Beachten Sie, dass die GCC-Erweiterung '__attribute__ ((gepackt))' hier auf die Mitglieder angewendet werden kann , ist aber orthogonal zu diesem Problem (es ergibt sich eine Struktur der Größe 4 + 1 = 5, dh mit dem gleichen Problem.) – ndkrempel

+0

Related: http://stackoverflow.com/questions/308364/c-bitfield-packing-with -bols (bezieht sich aber auf C++, das in Bitform-Feldern nicht ganz so genau formuliert ist.) – ndkrempel

+0

Laut einer Antwort auf die oben verlinkte Frage trat dieses Verhalten in gcc 4.2.4 nicht auf, so kann es sein eine Regression seither. – ndkrempel

Antwort

10

Das beschriebene Verhalten ist inkompatibel mit der C99 und C11-Standards, ist aber für die binäre Kompatibilität mit dem MSVC Compiler bereitgestellt (das ungewöhnliche struct Packverhalten hat.)

Glücklicherweise kann es entweder im Code mit __attribute__((gcc_struct)) auf die Struktur angewandt werden, deaktiviert oder mit dem Befehlszeilenschalter -mno-ms-bitfields (siehe documentation).

+0

Ist das irgendwo dokumentiert? Ich kann nicht scheinen, etwas nützliches auf "mno-ms-bitfields" zu finden –

+0

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

+0

Danke, nicht sicher, warum ich könnte das vorher nicht finden. Es wäre nützlich, das in Ihre Antwort zu schreiben, da Kommentare nicht langfristig gedacht sind. –

0

Mit beide GCC 4.7.1 (Eigenbau) und GCC 4.2.1 (LLVM/Klirren †) auf Mac OS X 10.7.4 mit einer 64-Bit-Kompilierung dieser Code Ausbeuten 4 in -std=c99 Modus:

#include <stdio.h> 

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

Das ist die Hälfte der Größe, die Sie unter Windows melden. Es scheint mir überraschend groß (ich würde erwarten, dass es eine Größe von 1 Byte ist), aber die Regeln der Plattform sind, was sie sind. Grundsätzlich ist der Compiler nicht verpflichtet, die Regeln zu befolgen, die Sie möchten; Es kann den Regeln der Plattform folgen, auf der es ausgeführt wird, und wo es die Chance hat, kann es sogar die Regeln der Plattform definieren, auf der es ausgeführt wird.

Das folgende Programm hat leicht fragwürdiges Verhalten (weil es greift u.i nach u.s wurde zuletzt geschrieben), zeigt aber, dass das Feld a in dem 4 niedrigstwertigen Bits gespeichert wird und das Feld b wird im nächsten Bit gespeichert:

#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; 
} 

Ausgang:

4 
4 
0x00000015 
0x0000001C 

† i686-Apfel-darwin11-llv m-gcc-4.2 (GCC) 4.2.1 (Basierend auf Apple Inc. Build 5658) (LLVM build 2336.9.00)