Написано ИИ:
В языках семейства C битовые поля появились как попытка описывать аппаратные регистры и сетевые пакеты «в лоб», через структуру.
struct { uint32_t dlc : 4; uint32_t ide : 1; uint32_t : 3; uint32_t fi : 8; };
Но за внешней простотой скрывается несколько неприятных особенностей:
В языках вроде Oberon подход обычно другой: вместо специальных синтаксических конструкций предлагается работать с числом как с упакованным контейнером значений.
Это оказывается не только проще и переносимее, но и значительно более общим методом.
Допустим, есть 32-битное значение:
31 0 +--------+---+--------+ | fi |ide| dlc | +--------+---+--------+
Пусть:
dlc занимает 4 бита;ide занимает 1 бит;fi занимает 8 бит.
Тогда всё это можно хранить в одном INTEGER.
Вместо специальных битовых полей используются обычные операции:
MODDIV+-Пусть:
dlc = 9 ide = 1 fi = 0ABH
Размещение:
| Поле | Смещение |
|---|---|
| dlc | 0 |
| ide | 4 |
| fi | 8 |
Тогда:
MODULE Example;
IMPORT Out;
PROCEDURE Go*;
VAR
dlc: INTEGER;
ide: INTEGER;
fi: INTEGER;
v: INTEGER;
BEGIN
dlc := 9;
ide := 1;
fi := 0ABH;
v := dlc
+ ide * 10H
+ fi * 100H;
Out.Int(v, 8);
Out.Ln
END Go;
END Example.
Получится:
0000AB19
Потому что:
dlc = 9 -> 00000009
ide = 1<<4 -> 00000010
fi = AB<<8 -> 0000AB00
------------------------
0000AB19
Поле занимает младшие 4 бита.
dlc := v MOD 10H;
Почему это работает?
v = q * 16 + r
где остаток r всегда находится в диапазоне 0..15.
То есть MOD 10H отсекает всё старшее.
Сначала сдвигаем значение вправо делением:
ide := v DIV 10H MOD 2;
Или по шагам:
v DIV 10H
перемещает бит ide в младшую позицию.
После этого:
MOD 2
оставляет только его.
fi := v DIV 100H MOD 100H;
Здесь:
DIV 100H убирает младшие 8 бит;MOD 100H оставляет только следующие 8.Это особенно интересно.
Нужно:
Пусть новое значение:
newDlc = 5
Тогда:
v := v - (v MOD 10H); v := v + 5;
v := v - (v DIV 10H MOD 2) * 10H; v := v + 1 * 10H;
v := v - (v DIV 100H MOD 100H) * 100H; v := v + 55H * 100H;
Вся схема основана на позиционной записи числа.
Фактически это работа с цифрами числа в системе счисления.
Для двоичных полей основание равно:
2^ширина_поля
Например:
| Размер поля | Основание |
|---|---|
| 1 бит | 2 |
| 4 бита | 16 |
| 8 бит | 256 |
Поэтому:
MOD base
извлекает поле, а
DIV base
сдвигает к нему.
И тут начинается самое интересное.
Битовые поля жёстко привязаны к степеням двойки.
Но арифметическая упаковка работает вообще с любыми диапазонами.
Например:
секунда : 0..59 минута : 0..59 час : 0..23
Можно хранить время одним числом.
v := sec + min * 60 + hour * 60 * 60;
Секунды:
sec := v MOD 60;
Минуты:
min := v DIV 60 MOD 60;
Часы:
hour := v DIV (60 * 60) MOD 24;
Это уже не двоичная упаковка, а универсальная позиционная схема хранения.
Код не зависит от:
Все смещения и диапазоны описаны явно.
Нет скрытой магии компилятора.
Работает: