Битовые поля
Битовые поля обеспечивают удобный доступ к отдельным битам данных. Они позволяют формировать объекты с длиной, не кратной байту. Что в свою очередь позволяет экономить память, более плотно размещая данные.
Битовое поле не может существовать само по себе. Оно может быть только элементом структуры или объединения. Например, в рамках структуры битовые поля имеют следующую форму определений:
1
2
3
4
5
6
7
struct имя_структуры
{
тип1 имя_поля1 : ширина_поля1;
тип2 имя_поля2 : ширина_поля2;
//..............
типi имя_поляi : ширина_поляi;
}
В данном случае типi представляет тип поля. В качестве типа поля может использоваться только int, допустимы модификаторы signed и unsigned. имя_поляi - это произвольный идентификатор, а ширина_поляi - положительное целое число, которое не должно превышать длину машинного слова для конкретной платформы (машинное слово измеряется в битах или байтах и равно разрядности регистров процессора, например, для архитектуры 64x - длина 64 бита).
Например, определим структуру с битовыми полями:
1
2
3
4
5
struct point
{
unsigned int x:5; // 0-31
unsigned int y:3; // 0-7
};
Структура point содержит два битовых поля. Первое поле x имеет ширину в 5 бит. То есть оно может принимать значения от 0 до 31. Второе поле - y - имеет ширину в 3 бита и может принимать значения от 0 до 7.
Затем мы сможем работать с этой структурой и ее элементами как и с любой структурой:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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 в двоичной системе).
В принципе мы сами можем программным образом узнать размещение полей в памяти. Для этого воспользуемся объединениями:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#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