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 байти щоб коректно відобразити дані.

 


Споріднені теми