2012-07-01 2 views
9

Работа в С11, следующей структуры:ошибка в реализации GCC битовых полей

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 что:

Реализация может выделить любой адресуемый блок хранения достаточно большой, чтобы держать битовое поле. Если остается достаточно места, бит-поле, которое сразу следует за другим битовым полем в структуре, должно быть упаковано в соседние биты того же блока.

Поэтому я считаю, что член b выше, должен был упакован в блок памяти, выделенный для члена a, в результате чего структур от общего размера 4 байта.

НКУ ведет себя правильно и упаковка имеет место при использовании тех же типов для двух членов, или когда один unsigned и другой signed, но типы unsigned и _Bool, кажется, считаются слишком отличается от GCC для того, чтобы справиться с ними правильно.

Может кто-то подтвердить мою интерпретацию стандарта и что это действительно ошибка GCC?

Меня также интересует обход (некоторый переключатель компилятора, прагма, __attribute__ ...).

Я использую GCC 4.7.0 с -std=c11 (хотя другие параметры показывают такое же поведение.)

+0

Обратите внимание, что расширение GCC '__attribute__ ((упакованное))' может применяться к членам здесь , но ортогонален этой проблеме (в результате получается структура размером 4 + 1 = 5, то есть с той же проблемой.) – ndkrempel

+0

Связано: http://stackoverflow.com/questions/308364/c-bitfield-packing-with -bools (но относится к C++, что не так требовательно в его формулировке по битовым полям.) – ndkrempel

+0

Согласно ответу на вопрос, связанный выше, это поведение не произошло в gcc 4.2.4, поэтому может быть регрессия с тех пор. – ndkrempel

ответ

10

Описанное поведение несовместимо с нормами C99 и C11, но обеспечивается двоичная совместимость с компилятором MSVC (который имеет необычное поведение структура упаковки.)

к счастью, его можно отключить либо в коде с __attribute__((gcc_struct)) применительно к структуре или с параметром командной строки -mno-ms-bitfields (см documentation).

+0

Насколько это документировано? Я не могу найти ничего полезного в 'mno-ms-bitfields' –

+0

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

+0

Спасибо, не знаю, почему я мог не находите этого раньше. Было бы полезно отредактировать это в вашем ответе, поскольку комментарии не должны быть долгосрочными. –

0

Использование и GCC 4.7.1 (самодельные) и GCC 4.2.1 (LLVM/лязг †) на 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-яблоко-darwin11-LLv m-gcc-4.2 (GCC) 4.2.1 (основано на Apple Inc. build 5658) (LLVM build 2336.9.00)

Смежные вопросы