2013-10-11 5 views
4

Программа находится на C, используя std = c99, это на 64-битной машине.Почему GCC использует это битовое поле?

struct epochs { 
    volatile unsigned int epoch : 1; 
    volatile unsigned int pulse : 1; 
    volatile unsigned int active0 : 7; 
    volatile unsigned int active1 : 7; 
    volatile unsigned int counter0 : 24; 
    volatile unsigned int counter1 : 24; 
}; 

когда я проверяю SizeOf (эпохи) это дает мне 12.

я могу сказать GCC не раздуть его, добавив __attribute ((упакована)); поэтому я могу обойти это. Однако мне бы очень хотелось знать, почему 4 байта добавлены для заполнения этой 64-битной структуры?

Главное, что эта структура НЕОБХОДИМА, чтобы быть 64 битами, потому что она обновляется сразу в 64-разрядных операциях атомного свопа, что, конечно же, не будет работать на 12-байтовом значении.

+4

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

+1

Не используйте битовые поля, если вы хотите переносимое/воспроизводимое поведение. –

+1

'struct epochs {unsigned int counter0: 24, pulse: 1, active0: 7, counter1: 24, epoch: 1, active1: 7; }; '8 байтов на моем Linux, Solaris и AIX. (кстати, почему все они были «неустойчивыми»?) – Cubbi

ответ

12
volatile unsigned int epoch : 1; 
volatile unsigned int pulse : 1; 
volatile unsigned int active0 : 7; 
volatile unsigned int active1 : 7; 

^32-бит (4 байта)

volatile unsigned int counter0 : 24; 

^32-бит (4 байта)

volatile unsigned int counter1 : 24; 

^32-бит (4 байта)

Так Еще 4 байта.

C говорит:

(C99, 6.7.2.1p10) «Если достаточно места остается битовое поле, что непосредственно следует другой битовое поле в структуре должны быть упакованы в соседние биты одного и того же блок»

Существует не достаточно места, чтобы поместить 24-бит (counter0) больше в 32-битном модуле (вероятно, размер unsigned int в вашей системе), которая уже владеет 16-битную (epoch, pulse, active0 , active1).

Вы можете использовать uin64_t вместо unsigned int, чтобы упаковать ваши битовые поля в 64-разрядной единице, но она определена реализацией независимо от того, поддерживает ли ваша система или нет.

(C99, 6.7.2.1p4) «Бит-поле должно иметь тип, который является квалифицированным или неквалифицированным версия _Bool, подписанная INT, неподписанных Int, или какой-либо другой реализации определенных типа

+2

Я думаю, что это было бы яснее, если бы отметили, что первые четыре поля используют 16 бит, а затем явно сказали, что поместить следующие 24 бита сразу после них пересекут 32-битную границу. И этот GCC, вероятно, избегает этого, потому что для доступа к битовому полю, пересекающему границу, потребуются две инструкции загрузки (в зависимости от целевого процессора) и дополнительные инструкции по обработке бит для объединения загруженных значений. И хранение через границу будет еще хуже. Таким образом, 32-битные блоки предпочтительны для распределения бит-полей. –

+2

Этот ответ - именно то, что мне нужно. Упорядочение полей на основе 32-битного выравнивания также означает, что я могу избежать заполнения: counter0, epoch, active0, counter1, pulse, active1. Это заставляет его придерживаться 64-бит. – Exodist

1

В то время как некоторые старые компиляторы использовали для оценки int foo:3; как синонимы, например. long foo:3, или short foo:3, и просто место foo, каким бы способом это ни было удобно, настоящий стандарт C указывает, что каждое поле бит должно полностью помещаться в единицу хранения соответствующего размера. Я понятия не имею, какое обоснование для этой спецификации, поскольку заданные битовые поля остаются слишком расплывчатыми, чтобы позволить их использование в переносном коде, но иногда делает невозможным оптимальное упаковывание вещей. Например, единственный способ эффективного хранения 24-битного значения внутри структуры - либо иметь машину, которая на машине, которая поддерживает 32-битные целые числа, либо иметь 8 бит данных, которые могут быть размещены рядом с 24-битное значение (предшествующее или следующее), чтобы «заполнить» 32-битное слово.

К счастью, в вашем конкретном случае можно избежать неэффективности, переставив поля.Также может быть возможно избежать неэффективности, изменив объявленный тип каждого поля на unsigned long long, если ваш компилятор поддерживает битовые поля с использованием такого типа [в этом случае битовые поля будут иметь возможность оседлать 32-разрядные границы при условии, что они не перешагнули 64-битная граница].