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


Я работаю над turbo C в windows, где char занимает один байт.Теперь моя проблема с нижеследующим Союзом.

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

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

Для struct нормально давать 1 байт, но это объединение работает неуместно.

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

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

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

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

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

6 ответов:

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

текст Alt

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

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

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

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

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

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

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

Вот что стандарт 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. Блок памяти bitfield равен 2.

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

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

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

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

#pragma pack(1)

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

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

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

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

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

Также обратите внимание, что стандарт гласит, что "битовое поле должно иметь тип, который является полной или неполной версией int, unsigned int или signed int", поэтому наличие битового поля в типе char является нестандартным.

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


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

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

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

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

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