Почему размер этого объединения равен 2 с битовыми полями? - PullRequest
3 голосов
/ 14 ноября 2008

Я работаю над Turbo C в Windows, где char занимает один байт. Теперь моя проблема с приведенным ниже объединением.

union a
{
 unsigned char c:2;
}b;
void main()
{
printf("%d",sizeof(b));  \\or even sizeof(union a)
}

Эта программа печатает выходные данные как 2, где объединение должно занимать только 1 байт. Почему это так?

для структуры это нормально, давая 1 байт, но этот союз работает ненадлежащим образом.

И еще один способ доступа к этим битовым полям.

scanf("%d",&b.c);  //even scanf("%x",b.c);

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

int x;
scanf("%d",&x);
b.c=x;

не можем ли мы избежать этого ?? есть ли другой способ ???

Ответы [ 6 ]

9 голосов
/ 14 ноября 2008

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

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

4 голосов
/ 14 ноября 2008

Turbo C основан на микропроцессоре 8086, который имеет двухбайтовую границу слова . Атомарное чтение и запись обычно привязаны к архитектуре процессора, поэтому компилятор добавляет несколько свободных байтов для выравнивания структуры данных.

alt text

Вызов #pragma pack(1) может отключить его, но не уверен, работает ли он на Turbo C.

1 голос
/ 01 февраля 2010

В ответах много дезинформации, поэтому уточню. Это может быть по одной из двух причин (я не знаком с компилятором).

  1. Единица хранения битового поля - 2.

  2. Выравнивание выполняется по границе слова (2 байта).

Я сомневаюсь, что это первый случай, так как это обычное расширение, которое принимает единицу хранения битового поля в качестве размера объявленного «базового» типа. В этом случае типом является тип char, который всегда имеет размер 1.

[В стандарте вы можете объявлять только битовые поля типа int или unsigned int, и «единица хранения», в которой сгруппированы битовые поля, является фиксированной (обычно такой же размер, как и int). Даже одно битовое поле будет использовать одну единицу хранения.]

Во 2-м случае для компиляторов C характерно применение #pragma pack, чтобы разрешить управление выравниванием. Я подозреваю, что упаковка по умолчанию равна 2, и в этом случае в конце объединения будет добавлен байт pad. Чтобы избежать этого, используйте:

#pragma pack(1)

Вы должны также использовать #pragma pack() впоследствии, чтобы установить значение по умолчанию (или даже лучше использовать аргументы push и pop, если это поддерживается вашим компилятором).

Всем респондентам, которые сказали, что вы должны мириться с тем, что делает компилятор, это противоречит духу C. Вы должны иметь возможность использовать битовые поля для отображения на любой размер или битовый порядок в ситуациях, когда у вас нет контроль над ним, такой как формат файла или аппаратное отображение.

Конечно, это очень непереносимо, поскольку разные реализации имеют разные порядки байтов, порядки добавления битов в единицу хранения битовых полей (сверху или снизу), размер единиц хранения, выравнивание по умолчанию и т. Д.

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

1 голос
/ 14 ноября 2008

В стандарте есть абзац, в котором говорится, что перед первым членом структуры не должно быть отступов. Но это прямо не говорит о профсоюзах. Разница в размере может возникнуть из-за того, что она хочет выровнять объединение по 2-байтовым границам, но, поскольку она не может заполнить перед первым членом структуры, структура будет иметь выравнивание по одному байту. Также обратите внимание, что у профсоюза может быть больше членов с разными типами, что может расширить требуемое выравнивание вашего союза. У компилятора могут быть причины для выравнивания по крайней мере 2 байта, например, чтобы упростить код, который должен обрабатываться в соответствии с требуемым значением объединения.

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

Вот что говорит стандарт C на ваш второй вопрос:

The operand of the unary & operator shall be either a function designator or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.

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

void func(void) { struct bits f; { int x; scanf("%d", &x); f.bitfield = x; } /* ... */ }
1 голос
/ 14 ноября 2008

Я не уверен, где вы найдете требование, чтобы объединение было точно минимальным размером. Объект должен быть как минимум такого же размера, как его члены, но это только нижняя граница.

Вы не можете взять адрес битового поля; какой будет его тип? Это не может быть int *. scanf (% d) запишет sizeof (int) * CHAR_BIT битов в int *, который вы передаете. Это записывает более 2 бит, но у вас нет этого места.

0 голосов
/ 14 ноября 2008

В дополнение к тому факту, что «в конце структуры или объединения может быть также и безымянное заполнение», компилятору разрешается помещать битовое поле в «любой адресуемый блок памяти, достаточно большой, чтобы содержать битовое поле» , (обе цитаты взяты из стандарта C90 - есть аналогичные, но разные формулировки для стандарта C99).

Также обратите внимание, что в стандарте сказано, что «битовое поле должно иметь тип, который является квалифицированной или неквалифицированной версией int, unsigned int или sign int», поэтому наличие битового поля в типе char не является стандарт.

Поскольку поведение битовых полей так сильно зависит от неопределенных деталей реализации компилятора (есть несколько других непереносимых проблем с битовыми полями, о которых я не упоминал), их использование почти всегда является плохой идеей. В частности, они являются плохой идеей, когда вы пытаетесь смоделировать битовые поля в формате файла, сетевого протокола или аппаратного регистра.


Больше информации от другого ответа SO :

В общем, вам следует избегать битовых полей и использовать другие манифестные константы (перечисления или что-то) с явным битом маскировка и смещение для доступа к «подполя» в поле.

Вот одна из причин, почему битовые поля должны следует избегать - они не очень портативны между компиляторами даже для того же Платформа. из стандарта C99 (есть похожая формулировка в C90 стандарт):

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

Вы не можете гарантировать, что немного поле будет «охватывать» границу int или нет, и вы не можете указать, является ли битовое поле начинается на нижнем конце int или верхний конец int (это не зависит от того, процессор с прямым порядком байтов или прямой порядок байтов).

...