Ошибка в реализации GCC битовых полей
работает в C11, следующая структура:
struct S {
unsigned a : 4;
_Bool b : 1;
};
получает выложил GCC как unsigned
(4 байта), из которых используются 4 бита, затем _Bool
(4 байта), из которых 1 бит используется для общего размера 8 байт.
обратите внимание, что C99 и C11 специально разрешают _Bool
как бит-поля. Стандарт C11 (и, вероятно, C99 тоже) также утверждает в §6.7.2.1 "структура и спецификаторы объединения" ¶11, что:
реализация может выделите любой адресуемый блок памяти, достаточно большой для хранения битового поля. Если остается достаточно места, то битовое поле, которое непосредственно следует за другим битовым полем в структуре, упаковывается в смежные биты той же единицы.
поэтому я считаю, что выше должны были быть упакованы в блок памяти, выделенный для члена a
, что приводит к структуре общего размера 4 байта.
GCC ведет себя правильно и упаковка происходит при использовании те же типы для двух членов, или когда один unsigned
и другие signed
, но unsigned
и _Bool
Кажется, считается слишком отличным от GCC, чтобы он правильно их обрабатывал.
может ли кто-нибудь подтвердить мою интерпретацию стандарта, и что это действительно ошибка GCC?
меня также интересует работа (некоторый переключатель компилятора, pragma,__attribute__
...).
я использую gcc 4.7.0 с -std=c11
(хотя другие настройки показывают то же самое поведение.)
2 ответов
описанное поведение несовместимо со стандартами C99 и C11, но предусмотрено для двоичной совместимости с компилятором MSVC (который имеет необычное поведение упаковки структуры.)
к счастью, его можно отключить либо в коде с __attribute__((gcc_struct))
в случае struct, или с командной строки -mno-ms-bitfields
(см. документация).
используя GCC 4.7.1 (home-built) и GCC 4.2.1 (LLVM/clang†) на Mac OS X 10.7.4 с 64-битной компиляцией, этот код дает 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;
}
это половина размера, который вы сообщаете в Windows. Он кажется мне удивительно большим (я бы ожидал, что он будет размером 1 байт), но правила платформы таковы, каковы они есть. В принципе, компилятор не обязан следовать правилам, которые вы хотите; он может следовать правилам платформы, на которой он работает, и там, где у него есть шанс, он может даже определить правила платформы, на которой он работает.
эта следующая программа имеет слегка сомнительное поведение (потому что она обращается к u.i
после u.s
был последний раз написан), но показывает, что поле a
хранится в 4 наименее значимых битах и поле b
хранится в следующем ролике:
#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;
}
выход:
4
4
0x00000015
0x0000001C
† i686-apple-darwin11-llvm-gcc-4.2 (GCC) 4.2.1 (на основе Apple Inc. строить 5658) (LLVM build 2336.9.00)