Ошибка в реализации 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)