C++. Битовые поля в структурах.

Битовые поля в структурах. Примеры


Содержание


Поиск на других ресурсах:

1. Битовые поля. Общие понятия. Синтаксис

Структуры позволяют удобно группировать данные (переменные) по некоторым критериям. В памяти такие данные, как правило, размещаются друг за другом согласно объявлению.

В языке C++ существует средство сжатия размера памяти, выделяемого под объявленные в структуре переменные. Это битовые поля в структурах.

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

Бывают случаи, когда для хранения данных размер памяти, который для них выделен, слишком большой. Например, чтобы хранить данные о дне недели, размера 1 байта или 8 бит слишком много. Существует 7 дней недели, поэтому достаточно 3 бита для представления этих данных (2 в степени 3 = 8 значений).

Объявление битового поля в структуре выглядит следующим образом

unsigned int field : value;

здесь

  • field – имя поля (переменной) в структуре;
  • value – количество біт, выделяемых для представления множества значений битового поля.

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

struct StructName
{
  unsigned int field_1 : value_1;
  unsigned int field_2 : value_2;
  ...
  unsigned int field_N : value_N
};

здесь

  • StructName – имя структуры;
  • field_1, field_2, field_N – названия переменных, которые соответствуют битовым полям структуры;
  • value_1, value_2, value_N – целочисленные значения, представляющие собой количество выделенных бит под размеры соответственно полей field_1, field_2, field_N.

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

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

 

2. Пример использования битовых полей в структуре Date, описывающей дату дня и номер дня в неделе

В задаче объявляется структура Day, описывающая следующие данные о дне:

  • номер дня в неделе от 1 до 7. Здесь 1 это понедельник, 2 – вторник и т.д., 7 – воскресенье;
  • число в месяце – 1..31;
  • номер месяца – 1..12;
  • год. Диапазон от 0 до 3000 года.

Для представления дня недели достаточно 3 бита, поскольку 3 бита позволяют получить 8 значений (2^3 = 8). То есть, 8 значений покрывают 7 значений, которые требуются для сохранения номера дня недели.

Исходя из вышеприведенных рассуждений о дне недели, можно сформировать количество бит для других полей структуры Day:

  • число в месяце достаточно представить 5 битами (2^5 = 32, 32>31);
  • номер месяца достаточно представить 4 битами (2^4 = 16, 16>12);
  • для сохранения года достаточно 12 бит (2^12 = 4096, 4096>3000).

Текст демонстрационной программы следующий

#include <iostream>
using namespace std;

struct Day
{
  unsigned int weekDay : 3; // день недели 1..7
  unsigned int number : 5;  // число 1..31
  unsigned int month : 4;   // номер месяца 1..12
  unsigned int year : 12;   // год 0..3000
};

void main()
{
  // Поля битов в структурах
  // 1. Объявить структуру
  Day d;

  // 2. Заполнить структуру данными 14.01.2022, пятница
  d.weekDay = 5;
  d.number = 14;
  d.month = 1;
  d.year = 2022;

  // 3. Вывести данные
  cout << "d.weekDay = " << d.weekDay << endl;
  cout << "d.number = " << d.number << endl;
  cout << "d.month = " << d.month << endl;
  cout << "d.year = " << d.year << endl;

  // 4. Вывести размер структуры
  cout << "sizeof(Day) = " << sizeof(d) << endl; // 4 байти
}

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

d.weekDay = 5
d.number = 14
d.month = 1
d.year = 2022
sizeof(Day) = 4

 

3. Пример сравнения структуры с битовыми полями со структурой, не использующей битовые поля. Структура Time

Пусть нужно реализовать структуру Time, описывающую временной интервал в виде

hours : minutes : seconds : milliseconds

здесь

  • hours – количество часов в одних сутках 0..23;
  • minutes – количество минут в часе 0..59;
  • seconds – количество секунд в минуте 0..59;
  • milliseconds – количество миллисекунд 0..999.

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

  • поле hours – 5 бит (2^5 = 32 > 23). Из 5 битов можно сформировать до 2^5=32 разных значений, что вполне достаточно для представления 24 значений этого поля;
  • minutes – 6 бит (2^6 = 64 > 59);
  • seconds – 6 бит (2^6 = 64 > 59);
  • milliseconds – 10 бит (2^10 = 1024 > 999).

В программе, с целью демонстрации, создаются две структуры Time и Time2, в которых реализованы необходимые поля для представления времени. Однако структура Time использует битовые поля, а структура Time2 использует обычные значения типа unsigned int.

Текст программы следующий

#include <iostream>
using namespace std;

// Структура Time использует битовые поля
struct Time
{
  unsigned int hours : 5;
  unsigned int minutes : 6;
  unsigned int seconds : 6;
  unsigned int milliseconds : 10;
};

// Структура Time2 не использует поля
struct Time2
{
  unsigned int hours;
  unsigned int minutes;
  unsigned int seconds;
  unsigned int milliseconds;
};

void main()
{
  // Сравнение структур с битовыми полями и без них
  // 1. Объявить переменные
  Time tm;
  Time2 tm2;

  // 2. Использование структур в программе
  // 2.1. Структура типа Time
  tm.hours = 12;
  tm.seconds = 36;
  tm.milliseconds = 777;
  tm.minutes = 3;

  // 2.2. Структура типа Time2
  tm2.hours = 20;
  tm2.seconds = 39;
  tm2.milliseconds = 555;
  tm2.minutes = 55;

  // 3. Определить и вывести размер структур Time, Time2
  //    с помощью оператора sizeof()
  size_t sizeTime = sizeof(Time);     // 4
  size_t sizeTime2 = sizeof(Time2);   // 16
  cout << "sizeTime = " << sizeTime << endl;
  cout << "sizeTime2 = " << sizeTime2 << endl;
}

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

sizeTime = 4
sizeTime2 = 16

Как видно из результата, различий между использованием обычных структур и битовых структур нет (доступ к полям, заполнение данными и т.п.). Однако, размер битовой структуры (переменная tm) в памяти занимает всего-навсего 4 байта по сравнению с размером обычной структуры в 16 байт.

В структуре Time происходит добавление битов в полях. Суммарное количество бит составляет

5 + 6 + 6 + 10 = 27

Если разделить 27 на 8 (количество бит в байте), то получится 3 с избытком. То есть, для представления 27 битов нужно 4 байта чтобы корректно отобразить данные.

 


Связанные темы