Битовые поля обеспечивают удобный доступ к отдельным битам данных. Они позволяют формировать объекты с длиной, не кратной байту. Что в свою очередь позволяет экономить память, более плотно размещая данные.
Битовое поле не может существовать само по себе. Оно может быть только элементом структуры или объединения. Например, в рамках структуры битовые поля имеют следующую форму определений:
struct имя_структуры { тип1 имя_поля1 : ширина_поля1; тип2 имя_поля2 : ширина_поля2; //.............. типi имя_поляi : ширина_поляi; }
В качестве типа поля может использоваться только int, но допустимы также модификаторы signed и unsigned. имя_поля представляет произвольный идентификатор, а ширина_поля - положительное целое число, которое не должно превышать длину машинного слова для конкретной платформы (машинное слово измеряется в битах или байтах и равно разрядности регистров процессора, например, для архитектуры 64x - длина 64 бита).
Например, определим структуру с битовыми полями:
struct point { unsigned int x:5; // 0-31 unsigned int y:3; // 0-7 };
Структура point содержит два битовых поля. Первое поле - x
имеет ширину в 5 бит. То есть оно может принимать значения от 0 до 31.
Второе поле - y
- имеет ширину в 3 бита и может принимать значения от 0 до 7.
Затем мы сможем работать с этой структурой и ее элементами как и с любой структурой:
#include <stdio.h> struct point { unsigned int x:5; // 0-31 unsigned int y:3; // 0-7 }; int main(void) { struct point center = {0, 5}; center.x = 2; printf("x=%d y=%d \n", center.x, center.y); // x=2 y=5 return 0; }
В зависимости от платформы расположение полей структуры в памяти может отличаться. В частности, на Windows порядок расположения следующий: поля в начале структуры имеют младшие адреса, а поля в конце структуры имеют старшие адреса. То есть если мы возьмем из примера выше поля x и y структуры point с финальными значениями x=2 и y=5, то мы получим на Windows следующее размещение битов в памяти:
Так как поля x и y вместо занимают 8 бит, то соответственно на картинке имеется 8 ячеек. На трех первых битах размещено поле point.y. Так как оно имеет значение 5, то в двоичной системе это будет 101. А на следующих 5 битах помещается поле point.x со значением 2 (то есть 10 в двоичной системе).
В принципе мы сами можем программным образом узнать размещение полей в памяти. Для этого воспользуемся объединениями:
#include <stdio.h> struct point { unsigned int x:5; // 0-31 unsigned int y:3; // 0-7 }; union code { struct point p; struct{ unsigned a0:1; unsigned a1:1; unsigned a2:1; unsigned a3:1; unsigned a4:1; unsigned a5:1; unsigned a6:1; unsigned a7:1; } byte; }; int main(void) { struct point center = {2, 5}; union code c; c.p = center; printf("7 \t 6 \t 5 \t 4 \t 3 \t 2 \t 1 \t 0 \n"); printf("%d \t %d \t %d \t %d \t %d \t %d \t %d \t %d \n", c.byte.a7, c.byte.a6, c.byte.a5, c.byte.a4, c.byte.a3, c.byte.a2, c.byte.a1, c.byte.a0); return 0; }
Объединение code содержит два элемента - структуры point и безымянную структуру byte. Как известно из прошлой темы, элементы структуры будут занимать одну и ту же область в памяти, точнее будут начинаться с одного и того же места в памяти. Поэтому для выяснения, какие биты заняты, безымянная структура byte имеет 8 полей, каждое из которых имеет ширину 1 бит.
И при запуске программы мы наглядно сможем увидеть размещение значений в памяти:
7 6 5 4 3 2 1 0 1 0 1 0 0 0 1 0