В отличие от некоторых других компьютерных языков, в языке С имеется встроенная поддержка битовых полей[1], которая дает возможность получать доступ к единичному биту. Битовые поля могут быть полезны по разным причинам, а именно:
Хотя для решения этих задач можно успешно применять побитовые операции, битовые поля могут придать вашему коду больше упорядоченности (и, возможно, с их помощью удастся достичь большей эффективности).
Битовое поле может быть членом структуры или объединения. Оно определяет длину поля в битах. Общий вид определения битового поля такой:
тип имя : длина;
Здесь тип означает тип битового поля, а длина — количество бит, которые занимает это поле. Тип битового поля может быть int, signed или unsigned. (Кроме того, в соответствии со стандартом С99, у битового поля еще может быть тип _Вооl.)
Битовые поля часто используются при анализе данных, поступающих в программу с аппаратуры. Например, в результате опроса состояния адаптера последовательной связи может возвращаться байт состояния, организованный следующим образом:
Бит | Что означает, если установлен |
---|---|
0 | Изменение в линии сигнала разрешения на передачу (change in clear-to-send line) |
1 | Изменение состояния готовности устройства сопряжения (change in data-set-ready) |
2 | Обнаружена концевая запись (trailing edge detected) |
3 | Изменение в приемной линии (change in receive line) |
4 | Разрешение на передачу. Сигналом CTS (clear-to-send) модем разрешает подключенному терминалу передавать данные |
5 | Модем готов (data-set-ready) |
6 | Телефонный вызов (telephone ringing) |
7 | Сигнал принят (received signal) |
Информацию в байте состояния можно представить с помощью следующего битового поля:
struct status_type { unsigned delta_cts: 1; unsigned delta_dsr: 1; unsigned tr_edge: 1; unsigned delta_rec: 1; unsigned cts: 1; unsigned dsr: 1; unsigned ring: 1; unsigned rec_line: 1; } status;
Для того чтобы программа могла определить, когда можно отправлять или принимать данные, можно использовать такие операторы:
status = get_port_status(); if(status.cts) printf("Разрешение на передачу"); if(status.dsr) printf("Данные готовы");
Для присвоения битовому полю значения используйте тот же способ, что и для элемента, находящегося в структуре любого другого типа. Вот, например, фрагмент кода, выполняющий сброс поля ring:
status.ring = 0;
Как видно из этого примера, каждое битовое поле доступно с помощью оператора точка. Однако если структура передана с помощью указателя, то следует использовать оператор стрелка ->.
Нет необходимости давать имя каждому битовому полю. Таким образом можно легко получать доступ к нужному биту, обходя неиспользуемые. Например, если вас интересуют только биты cts и dsr, то структуру status_type можно объявить таким образом:
struct status_type { unsigned : 4; unsigned cts: 1; unsigned dsr: 1; } status;
Кроме того, обратите внимание, что если биты, расположенные после dsr, не используются, то определять их не надо.
В структурах можно сочетать обычные члены с битовыми полями. Например, в структуре
struct emp { struct addr address; float pay; unsigned lay_off: 1; /* временно уволенный или работающий */ unsigned hourly: 1; /* почасовая оплата или оклад */ unsigned deductions: 3; /* налоговые (IRS) удержания */ };
определены данные о работнике, для которых выделяется только один байт, содержащий информацию трех видов: статус работника, на окладе ли он, а также количество удержаний из его зарплаты. Без битового поля эта информация занимала бы 3 байта.
Использование битовых полей имеет определенные ограничения. Нельзя получить адрес битового поля. Нет массивов битовых данных. При переносе кода на другую машину неизвестно, будут ли поля обрабатываться справа налево или слева направо; это значит, что выполнение любого кода, в котором используются битовые поля, в определенной степени может зависеть от машины, на которой он выполняется. Другие ограничения будут зависеть от конкретных реализаций.
[1]Называются также полями битов.