Битовые поля

Последнее обновление: 10.01.2023

Битовые поля обеспечивают удобный доступ к отдельным битам данных. Они позволяют формировать объекты с длиной, не кратной байту. Что в свою очередь позволяет экономить память, более плотно размещая данные.

Битовое поле не может существовать само по себе. Оно может быть только элементом структуры или объединения. Например, в рамках структуры битовые поля имеют следующую форму определений:

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
Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850