
3.5. Битовые поля
Стандарт C99, который часто является основой языка С, позволяет описывать данные на уровне битов. Это достигается путем использования битовых полей, представляющие собой переменные типов signed или unsigned int, у которых используются лишь несколько бит для хранения данных. Такие переменные обычно записываются в структуру и единую последовательность бит. Рассмотрим пример, в котором задается структура flags, внутри которой задано 8 битовых полей:
struct { unsigned int first : 1; unsigned int second : 1; unsigned int third : 1; unsigned int forth : 1; unsigned int fifth : 1; unsigned int sixth : 1; unsigned int sevnth : 1; unsigned int eighth : 1; } flags;
Теперь, для определения того или иного бита переменной flags достаточно воспользоваться операцией
flags.first = 1; flags.third = 1;
В этом случае будут установлены первый и третий биты, а остальные равны нулю, что соответствует числу 5. Данное значение можно отобразить, воспользовавшись функцией printf():
printf(“flags = %d.\n”,flags);
но переменной flags нельзя присваивать значения как обычной переменной, т.е. следующий программный код будет неверным:
flags = 5; //неверно, так нельзя
Также нельзя присваивать значение flags переменным, например, следующая запись приведет к сообщению об ошибке:
int var = flags; //ошибка, структуру нельзя присваивать переменной
Так как поля first,…, eighth могут содержать только один бит информации, то они принимают значения 0 или 1 для типа unsigned int и 0 и -1 - для типа signed int. Если полю присваивается значение за пределами этого диапазона, то оно выбирает первый бит из присваиваемого числа.
В общем случае можно задавать любое число бит для описания полей, например
struct { unsigned int code1 : 2; unsigned int code2 : 2; unsigned int code3 : 8; } prcode;
Здесь создается два двух битовых поля и одно восьмибитовое. В результате возможны следующие операции присваивания:
prcode.code1 = 0; prcode.code2 = 3; prcode.code3 = 128;
Структуры flags и prcode удобно использовать в условных операторах if и switch. Рассмотрим пример использования структуры битовых полей для описания свойств окна пользовательского интерфейса.
struct tag_window { unsigned int show : 1 //показать или скрыть unsigned int style : 3 //WS_BORDER, WS_CAPTION, WS_DLGFRAME unsigned int color : 3 //RED, GREEN, BLUE } window;
Определим следующие константы:
#define WS_BORDER 1 //001 #define WS_CAPTION 2 //010 #define WS_DLGFRAME 4 //100 #define RED 1 #define GREEN 2 #define BLUE 4 #define SW_SHOW 0 #define SW_HIDE 1
Пример инициализации структуры битовых полей
window.show = SW_SHOW; window.style = WS_BORDER | WS_CAPTION; window.color = RED | GREEN | BLUE; //белый цвет
3.6. Объединения
Еще одним важным типом представления данных являются объединения. Это тип данных, который позволяет хранить различные типы данных в одной и той же области памяти (начиная с одного и того же адреса в памяти). Объединение задается с помощью ключевого слова union подобно структуре. Покажем особенность применения объединений на примере хранения данных, которые могут быть и вещественными и целыми или представлять собой символ. Так как наперед неизвестно какой тип данных требуется сохранять, то в объединении необходимо задать три поля:
union tag_value { int var_i; double var_f; char var_ch; };
Данное объединение будет занимать в памяти 8 байт, ровно столько, сколько занимает переменная самого большого объема, в данном случае var_f типа double. Все остальные переменные var_i и var_ch будут находиться в той же области памяти, что и переменная var_f. Благодаря такому подходу происходит экономия памяти, но платой за это является возможность хранения значения только одной переменной из трех в определенный момент времени. Как видно из постановки задачи такое ограничение не будет влиять на работоспособность программы. Если бы вместо объединения использовалась структура tag_value, то можно было бы сохранять значения всех трех переменных одновременно, но при этом требовалось бы больше памяти.
Для того чтобы программа «знала» какой тип переменной содержит объединение tag_value, необходимо ввести переменную, значение которой будет указывать номер используемой переменной: 0 – var_i, 1 – var_f и 2 – var_ch. Причем эту переменную удобно представить с объединением в виде структуры следующим образом:
struct tag_var { union tag_value value; short type_var; };
Таким образом, получаем следующий алгоритм записи разнородной информации в объединение tag_value.