2012-07-01 13 views
9

pracujących w C11, następujące struktury:Błąd w realizacji GCC bitowych pól

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

pobiera rozplanowane przez GCC JAKO unsigned (cztery bajty), które są wykorzystywane 4 bity, a następnie _Bool (4 bajty), z których używany jest 1 bit, o łącznej wielkości 8 bajtów.

Należy zauważyć, że C99 i C11 wyraźnie zezwalają na _Bool jako element pola bitowego. Średnia C11 (i prawdopodobnie też C99) stwierdza również pod §6.7.2.1 „Struktura i unia Specyfikatory” ¶11 że:

implementacja może przeznaczyć dowolną adresowalny moduł pamięci na tyle duże, aby pomieścić nieco pola. Jeśli pozostanie wystarczająca ilość miejsca, pole bitowe, które następuje bezpośrednio po innym polu bitowym w strukturze, zostanie zapakowane w sąsiednie bity tego samego urządzenia.

Więc wierzę, że człon b powyżej powinny zostały zapakowane do jednostki magazynowej przeznaczonej na członka a, w wyniku struct całkowitej wielkości 4 bajtów.

GCC zachowuje się poprawnie i pakowania występuje podczas korzystania z tych samych typów dla dwóch członków, lub gdy jeden jest unsigned a drugi signed, ale typy unsigned i _Bool wydają się być uważany za odrębny przez GCC na to, aby je obsłużyć prawidłowo.

Czy ktoś może potwierdzić moją interpretację standardu, i że jest to rzeczywiście błąd GCC?

Interesuje mnie również obejście (jakaś zmiana kompilatora, pragma, __attribute__ ...).

Używam gcc 4.7.0 z -std=c11 (choć inne ustawienia wykazują takie samo zachowanie.)

+0

Należy zauważyć, że rozszerzenie GCC "__attribute__ ((spakowane))" może być stosowane do członków tutaj , ale jest ortogonalny dla tego problemu (powoduje to, że struktura 4 + 1 = 5, czyli z tym samym problemem). – ndkrempel

+0

Powiązane: http://stackoverflow.com/questions/308364/c-bitfield-packing-with -bools (ale odnosi się do C++, co nie jest aż tak wymagające w swoim brzmieniu na polach bitowych). – ndkrempel

+0

Zgodnie z odpowiedzią na pytanie powiązane powyżej, to zachowanie nie wystąpiło w gcc 4.2.4, więc może być regresja od tego czasu. – ndkrempel

Odpowiedz

10

Opisane zachowanie jest niezgodne ze standardami C99 i C11, ale jest przewidziane zgodności binarnej z kompilatora MSVC (który ma niezwykłe zachowanie opakowania struct).

szczęście, to może być wyłączone albo w kodzie z __attribute__((gcc_struct)) przyłożonego do struktury, lub za pomocą przełącznika wiersza polecenia -mno-ms-bitfields (patrz documentation).

+0

Czy jest to udokumentowane w dowolnym miejscu? Nie mogę znaleźć niczego użytecznego na "mno-ms-bitfields" –

+0

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

+0

Dziękuję, nie wiem, dlaczego mogłem nie znajduję tego wcześniej. Przydałoby się edytować tę odpowiedź, ponieważ komentarze nie mają charakteru długoterminowego. –

0

Korzystanie zarówno GCC 4.7.1 (home-built) i GCC 4.2.1 (LLVM/dzyń †) na Mac OS X 10.7.4 z kompilacji 64-bitowej, plony ten kod 4 w trybie -std=c99:

#include <stdio.h> 

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

To połowa wielkości, którą raportujesz w systemie Windows. Wydaje mi się to zaskakująco duże (spodziewałem się, że będzie to rozmiar 1 bajta), ale zasady platformy są tym, czym są. Zasadniczo kompilator nie jest zobowiązany do przestrzegania reguł, które chciałbyś; może być zgodny z regułami platformy, na której jest uruchamiany, i gdzie ma taką możliwość, może nawet zdefiniować reguły platformy, na której działa.

Niniejszy program ma łagodnie wątpliwą zachowanie (ponieważ dostęp u.i po u.s ostatnio zapisywane), ale wskazuje, że pole a są przechowywane w 4 najmniej znaczących bitów i pole b jest magazynowany w następnej transmisji:

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

wyjściowa:

4 
4 
0x00000015 
0x0000001C 

† i686-jabłko-darwin11-LLV m-gcc-4.2 (GCC) 4.2.1 (na podstawie wersji 5658 firmy Apple Inc.) (kompilacja LLVM 2336.9.00)

Powiązane problemy