Перейти к содержанию

Последнее обновление:2026-10-12

C++ битовые поля

Об авторе:

Личный официальный аккаунт:

wechat

Что такое Bit field?

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

  • Размещение битового поля в памяти зависит от машины
  • Тип битового поля должен быть целочисленным или перечислением; поведение битовых полей в знаковых типах зависит от конкретной реализации
  • Оператор взятия адреса (&) неприменим к битовому полю; никакой указатель не может указывать на битовое поле класса

Использование битовых полей

Битовые поля обычно объявляются с помощью структуры; в таком объявлении каждому члену-битовому полю задаётся имя и определяется его ширина:

struct bit_field_name
{
    type member_name : width;
};
Elements Description
bit_field_name имя структуры битового поля
type тип члена битового поля; должен быть int, signed int или unsigned int
member_name имя члена битового поля
width количество бит, занимаемых членом

Например, объявим следующее битовое поле:

struct _PRCODE
{
    unsigned int code1: 2;
    unsigned int cdde2: 2;
    unsigned int code3: 8;
};
struct _PRCODE prcode;

Это определение делает prcode содержащим 2 двухбитовых поля и 1 восьмибитовое поле; мы можем присваивать им значения с помощью оператора доступа к членам структуры

prcode.code1 = 0;
prcode.code2 = 3;
procde.code3 = 102;

При присваивании следует следить, чтобы значение не превышало ёмкость члена битового поля: например, prcode.code3 — восьмибитовый член битового поля, его ёмкость составляет 2^8 = 256, то есть диапазон присваиваемых значений должен быть [0,255].

Размер и выравнивание битового поля

Размер битового поля

Например, следующее битовое поле:

struct box 
{
    unsigned int a: 1;
    unsigned int  : 3;
    unsigned int b: 4;
};

В середине этой структуры битового поля есть безымянное битовое поле, занимающее 3 бита; оно служит только заполнителем и не имеет практического смысла. Заполнитель делает так, что структура в целом использует 8 бит. Однако в языке C базовой единицей битового поля является unsigned int, поэтому даже если единственный член структуры — однобитовое поле, размер структуры будет таким же, как у unsigned int. В некоторых системах unsigned int занимает 16 бит, в системах x86 — 32 бита. Далее в статье по умолчанию считается, что unsigned int занимает 32 бита.

Выравнивание битового поля

Член битового поля не может пересекать границу двух unsigned int; если общее число бит, объявленных для членов, превышает размер одного unsigned int, компилятор автоматически сдвигает члены битового поля так, чтобы они были выровнены по границе unsigned int. Например:

struct stuff 
{
    unsigned int field1: 30;
    unsigned int field2: 4;
    unsigned int field3: 3;
};

field1 + field2 = 34 бита, что превышает 32 бита; компилятор переместит field2 в следующий unsigned int-блок, между stuff.field1 и stuff.field2 останется зазор в 2 бита, stuff.field3 идёт сразу после stuff.field2, и теперь размер структуры составляет 2 * 32 = 64 бита.

Эта «пустота» может быть заполнена упомянутым ранее безымянным членом битового поля; кроме того, можно использовать безымянный член битового поля нулевой ширины, чтобы следующий член битового поля был выровнен по следующему целому числу. Например:

struct stuff 
{
    unsigned int field1: 30;
    unsigned int       : 2;
    unsigned int field2: 4;
    unsigned int       : 0;
    unsigned int field3: 3; 
};

Здесь между stuff.field1 и stuff.field2 есть зазор в 2 бита, stuff.field3 же хранится в следующем unsigned int, и теперь размер структуры составляет 3 * 32 = 96 бит.

Учебный код см. в: bit.cpp

Инициализация битовых полей и переназначение битов

Инициализация

Инициализация битового поля выполняется так же, как инициализация обычной структуры; приведём два способа:

struct stuff s1= {20,8,6};

либо можно напрямую присвоить значения членам битового поля

struct stuff s1;
s1.field1 = 20;
s1.field2 = 8;
s1.field3 = 4;

Переназначение битового поля (Re-mapping)

Объявим битовое поле размером 32 бита

struct box {
    unsigned int ready:     2;
    unsigned int error:     2;
    unsigned int command:   4;
    unsigned int sector_no: 24;
}b1;

Обнуление битового поля с помощью переназначения

int* p = (int *) &b1;  // 将 "位域结构体的地址" 映射至 "整形(int*) 的地址" 
*p = 0;                // 清除 s1,将各成员归零

Переназначение 32-битового поля в unsigned int с помощью объединения (union)

Сначала кратко расскажем об объединении

«Объединение» — это особый класс, а также составной тип структуры данных. Внутри «объединения» можно определять различные типы данных; в переменную, объявленную как тип этого «объединения», допускается поместить любой из типов данных, определённых в «объединении»; эти данные используют одну и ту же память, чтобы экономить место

У «объединения» и «структуры» есть некоторое сходство, но между ними есть и принципиальная разница. В структуре каждый член имеет собственное пространство памяти, а общая длина структурной переменной равна сумме длин всех членов (кроме пустых структур и без учёта выравнивания границ). В «объединении» же все члены используют одно пространство памяти, а длина переменной объединения равна длине самого длинного из членов. Следует пояснить: под общим использованием здесь не понимается одновременное размещение нескольких членов в одной переменной объединения — имеется в виду, что переменной объединения может быть присвоено значение любого члена, но каждый раз только одно; присваивание нового значения стирает старое.

Мы можем объявить следующее объединение:

union u_box {
  struct box st_box;     
  unsigned int ui_box;
};

В системах x86 и unsigned int, и box занимают 32 бита; через это объединение st_box и ui_box используют одну область памяти. Какой именно бит битового поля соответствует какому биту unsigned int, зависит от компилятора и аппаратуры. Обнулим битовое поле с помощью объединения, код таков:

union u_box u;
u.ui_box = 0;

Материал подготовлен по: http://www.yuan-ji.me/C-C-%E4%BD%8D%E5%9F%9F-Bit-fields-%E5%AD%A6%E4%B9%A0%E5%BF%83%E5%BE%97/

Похожие темы

Комментарии