Bin рассматривать как битовое поле. Битовые поля
Последнее обновление: 03.06.2017
Битовые поля обеспечивают удобный доступ к отдельным битам данных. Они позволяют формировать объекты с длиной, не кратной байту. Что в свою очередь позволяет экономить память, более плотно размещая данные.
Битовое поле не может существовать само по себе. Оно может быть только элементом структуры или объединения. Например, в рамках структуры битовые поля имеют следующую форму определений:
Struct имя_структуры { тип1 имя_поля1: ширина_поля1; тип2 имя_поля2: ширина_поля2; //.............. типi имя_поляi: ширина_поляi; }
В данном случае типi представляет тип поля. В качестве типа поля может использоваться только int , допустимы модификаторы signed и unsigned. имя_поляi - это произвольный идентификатор, а ширина_поляi - положительное целое число, которое не должно превышать длину машинного слова для конкретной платформы (машинное слово измеряется в битах или байтах и равно разрядности регистров процессора, например, для архитектуры 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
В зависимости от платформы расположение полей структуры в памяти может отличаться. В частности, на Windows порядок расположения следующий: поля в начале структуры имеют младшие адреса, а поля в конце структуры имеют старшие адреса. То есть если мы возьмем из примера выше поля x и y структуры point с финальными значениями x=2 и y=5, то мы получим на Windows следующее размещение битов в памяти:
Так как поля x и y вместо занимают 8 бит, то соответственно на картинке имеется 8 ячеек. На трех первых битах размещено поле point.y. Так как оно имеет значение 5, то в двоичной системе это будет 101. А на следующих 5 битах помещается поле point.x со значением 2 (то есть 10 в двоичной системе).
В принципе мы сами можем программным образом узнать размещение полей в памяти. Для этого воспользуемся объединениями:
#include
Объединение code содержит два элемента - структуры point и безымянную структуру byte. Как известно из прошлой темы, элементы структуры будут занимать одну и ту же область в памяти, точнее будут начинаться с одного и того же места в памяти. Поэтому для выяснения, какие биты заняты, безымянная структура byte имеет 8 полей, каждое из которых имеет ширину 1 бит.
Структуры. Перечисления. Объединения. Битовые поля .
1.1. Перечисления.
Перечислением называется тип данных, который включает множество именованных целочисленных констант. Именованные константы, принадлежащие перечислению, называются перечислимыми константами.
Объявляются перечисления следующим образом:
enum color { r, g, b }; // объявление типа
где enum – ключевое слово, color – имя типа перечисления, r, g, b – сами перечислимые константы. При объявлении типа перечисления его значения могут инициализироваться произвольными целочисленными константами или константным выражением. Например,
enum color { r = 2, g = r = 2, b = 6 }; // объявление типа
Если инициализация отсутствует, то перечислимым константам присваиваются последовательные значения: 0, 1, 2, … Например,
enum color { r, g, b }; // r = 0, g = 1, b = 2
Переменная типа перечисление объявляется следующим образом:
enum color c; // объявление переменной c, которая имеет тип color (язык С)
color d; // объявление переменной d, которая имеет тип color (язык С++)
и также называется перечислением. Объявление типа перечисления и переменной, которая имеет этот тип, может быть объединено в одну инструкцию. Например,
enum turn { off, on } a; // а – переменная типа turn
Переменным перечислимого типа можно присваивать только именованные значения перечислимых констант. Например,
color c = r; // правильно
color c = 0; // ошибка
Целочисленным переменным можно присваивать значения перечислимых констант. Например,
int e = r; // правильно
1.2. Структуры.
Тип структура описывает упорядоченный набор данных, которые называются полями или членами структуры. Каждое поле структуры имеет имя и тип, который должен отличаться от типов void и функция. При этом следует учитывать, что структура может включать только такие поля, длина которых известна компилятору в момент определения структуры.
Объявляются структуры следующим образом:
где struct – ключевое слово, emp – имя типа структуры, empno, name, salary – члены структуры.
Переменная типа структура объявляется следующим образом:
struct emp e; // объявление переменной e, которая имеет тип emp (язык С)
emp d; // объявление переменной d, которая имеет тип emp (язык С++)
и также называется структурой. Объявление типа структуры и переменной, которая имеет этот тип, может быть объединено в одну инструкцию. Например,
} director; // director – переменная типа emp
Инициализируются структуры также как и массивы. Например,
struct emp a = { 10, “Paul”, 2000 };
Так как члены структуры описываются в блоке, то их имена принадлежат локальной области видимости внутри этого блока. Для доступа к элементу структуры используется оператор точка ‘.’. Например,
director.empno = 20;
strcpy(director.name, “John”); // нельзя выполнить присваивание, так как director.name - константа
director.salary = 3000;
Членами структуры могут быть массивы и другие структуры. Для доступа к членам вложенной структуры оператор ‘.’ используется столько раз, какова вложенность структуры. Например,
#include
struct demo c = { 1, {2, 3} };
printf("%d %d %d\n", c.a, c.b.b, c.b.c); // печатает: 1 2 3
При объявлении структура может содержать поля, тип которых является указателем на тип объявляемой структуры. Например,
struct node /* вершина двоичного дерева */
struct node *left; /* левый потомок */
struct node *right; /* правый потомок */
Структуры одного типа можно присваивать друг другу. В этом случае оператор присваивания выполняет по членное копирование структур. Например,
struct emp boss;
boss = director;
Отметим, что структуры даже одного типа нельзя сравнивать между собой.
1.3. Объединения.
Тип объединение описывает набор данных, которые называются элементами или членами объединения. Объединение позволяет хранить в одной и той же области памяти значения различных типов, которые соответствуют типам элементов объединения. Но в каждый данный момент времени в этой области памяти может храниться только одно значение. Отсюда следует, что длина памяти, распределяемой компилятором под объединение, равна наибольшей из длин членов этого объединения. Как и в случае структуры, члены объединения могут иметь любой тип за исключением типов void и функция.
Объявляются объединения следующим образом:
где union – это ключевое слово, num – имя типа объединения, n, f – члены объединения.
Переменная типа объединение объявляется следующим образом:
union num d; // объявление переменной d, которая имеет тип num (язык С)
num d; // объявление переменной d, которая имеет тип num (язык С++)
и также называется объединением. Объявление типа объединения и переменной, которая имеет этот тип, может быть объединено в одну инструкцию. Например,
} d; // d – переменная типа num
При объявлении переменной типа объединение её можно инициализировать значением, которое должно иметь тип первого члена объединения. Например,
union num d = { 1 }; // правильно, d = 1
union num d = { 1.0 }; // ошибка
Как и в случае со структурами, для доступа к элементу объединения используется оператор точка ‘.’. Например,
Объединения одного типа можно присваивать друг другу. В этом случае оператор присваивания выполняет по членное копирование объединений. Например,
Также как и структуры объединения нельзя сравнивать.
1.4. Битовые поля.
Битовым полем называется член структуры или объединения, который определяет последовательность бит. Битовое поле может иметь один из следующих типов: int, unsigned или signed. При объявлении битового поля после его имени указывается длина поля в битах. Например,
unsigned a1: 1; // 1 бит
signed b1: 3; // 3 бита
int c1: 6; // 6 бит
Длина битового поля должна быть неотрицательным целым числом и не должна превышать длины базового типа данных битового поля.
Инициализируются битовые поля так же, как и обычные элементы структуры. Например,
} s = { 0, 1 }; // a1 = 0, a2 = 1
Доступ к элементам битового поля осуществляется так же, как и доступ к обычным членам структуры. Например,
#include
printf("s.a = %u\n", s.a); // печать: s.a = 1
printf("s.b = %d\n", s.b); // печать: s.b = -1
printf("s.c = %d\n", s.c); // печать: s.c = -4
Битовые поля могут использоваться в выражениях точно так же, как и переменные базового типа битового поля. Обычно битовые поля используются для установки различных флагов.
1.5. Передача структур в функции.
Когда структура используется как параметр функции, то она передается в функцию по значению, как и принято в языке программирования С. Например,
void print(struct emp s) // или просто: void print(emp s)
printf(“%d %s %f\n”, s.empno, s.name, s.salary);
Для того чтобы функция могла изменить значения членов структуры, она должна получить указатель на эту структуру. Для доступа к членам структуры через указатели используется оператор ‘->’. Например,
void init_emp(emp *ps, int en, char *nm, double sal)
ps->empno = en;
strcpy(ps->name, nm);
ps->salary = sal;
Аналогично передаются в функции и объединения. Например, передадим в функцию указатель на объединение.
void print(char c, num *n)
printf(“n = %d\n”, n->n);
printf(“f = %f\n”, n->f);
Битовое поле
Битовое поле - в программировании число, занимающее некоторый набор битов, напрямую не адресуемый процессором. Например: при 8-битном байте первые два поля протокола - версия и IHL - будут битовыми полями. На машинах с 32-битным байтом все поля IP-пакета (кроме IP-адресов отправителя и получателя) будут битовыми.
Обращение к битовым полям требует дополнительных команд процессора для маскирования и сдвига, и потому медленнее обращений к словам/байтам. Поэтому битовые поля применяются для максимально полной упаковки информации в местах, где не важна скорость доступа к информации.
Компиляторы, как правило, ограничивают работу с битовыми полями только извлечением значения битового поля и записью значения в битовое поле, а само битовое поле воспринимается как беззнаковое число. Реальный порядок следования битовых полей в структуре является системно-зависимым: в одних компиляторах битовые поля могут быть расположены начиная с младших битов, а в других - со старших.
Операции над многобитовыми полями
Пусть в одном байте находятся три битовых поля: 1-битовые a и b , 2-битовое c и 4-битовое d , то есть .
[старший бит] d d d d c c b a [младший бит]
Например: при a =1, b =0, c =2=10 2 , d=5=0101 2 получаем x =01011001 2 =89.
Сборка одного числа из битовых полей
Двоичные компьютеры обычно имеют команды побитового сдвига, которые позволяют быстро умножать на степени двойки - 2, 4, 8 и т. д. Вместо сложения можно применить команду логического «ИЛИ». Таким образом, число x можно собрать и по-другому:
X = (d << 3) | (c << 2) | (b << 1) | a
Извлечение битового поля
Для извлечения битового поля нужно провести две операции:
- Умножить операцией логического «И» число на битовую маску - число, у которого в соответствующих разрядах единицы, а в остальных нули.
C = (x & 00001100b) >> 2
- Провести побитовый сдвиг вправо.
- Умножить операцией логического «И» число на битовую маску соответствующей длины.
Для младшего поля побитовый сдвиг не нужен, то есть:
A = x & 00000001b
Для старшего поля побитовый сдвиг сам по себе, без умножения на маску, очистит x от ненужных битов - то есть,
D = x >> 4
Замена битового поля
- Очистить x от предыдущего значения побитовым умножением на маску с нулями в соответствующих битах.
- Побитово сложить x с новым значением (сдвинутым на нужное количество битов)
Например, если нам нужно заменить d , то
Xnew = (x & 00001111b) | (d << 4)
Операции над однобитовыми полями
Поля a и b имеют длину 1 бит - это позволяет работать с ними несколько другими средствами.
Проверка отдельного бита
Для проверки надо побитово умножить x операцией «И» на маску, у которой одна единица - в соответствующей позиции. Если получился 0, бит равен 0.
B = ((x & 00000010b) != 0)
Проверка, равен ли единице хотя бы один бит из нескольких:
A_or_b = ((x & 00000011b) != 0)
Проверка, равны ли единице все биты из нескольких:
A_and_b = ((x & 00000011b) == 00000011b)
Установка битов
Для этого надо сложить операцией «ИЛИ» x с маской, у которой единицы в соответствующих позициях. Например, чтобы включить бит a :
X1 = x | 00000001b
Чтобы включить и a , и b :
X2 = x | 00000011b
Снятие битов
Чтобы снять один или несколько битов, надо сложить x операцией «И» с маской, у которой в соответствующих позициях нули. В частности, чтобы выключить бит b , нужно дать команду:
X3 = x & 11111101b
Переключение битов
Для переключения битов (с 0 на 1, с 1 на 0) надо сложить x командой «Исключающее ИЛИ » с маской, у которой в соответствующих позициях единицы. Например, бит b переключается так:
X4 = x ^ 00000010b
Операции над знаковыми полями в дополнительном коде
Существуют два способа хранения отрицательных целых чисел - знаковый бит и дополнительный код . В подавляющем большинстве современных машин применяется второй. При записи отрицательных чисел дополнительным кодом имеем:
1 = 11111111b -2 = 11111110b -3 = 11111101b -4 = 11111100b и т. д.
Считаем, что поля c и d имеют именно такой формат. Тогда поле c может хранить числа от −2=10 2 до 1=01 2 , а поле d - от −8=1000 2 до 7=0111 2 .
Сборка и замена чисел
Каждое из слагаемых (кроме старшего), чтобы оно не испортило более старшие разряды, требуется умножать на битовую маску соответствующей длины. В частности:
X = (d << 4) + ((c & 00000011b) << 2) + (b << 1) + a
Извлечение чисел
Для извлечения чисел требуется сдвинуть поле на нужное количество битов вправо, заодно размножив знаковый бит. Например, для этого можно воспользоваться арифметическим сдвигом . Если x имеет длину 8 битов, то
C = (x << 4) >>a 6 d = x >>a 4
Внимание! В языке программирования Java всё наоборот: знаком >> обозначается арифметический сдвиг, знаком >>> - простой.
Если арифметического сдвига нет, то…
C1 = x >> 2 если (c1 & 00000010b ≠ 0) то c = c1 | 0x11111100b иначе c = c1 & 0x00000011b
Объявления битовых полей
В языке C/C++
В декларации битового поля используется двоеточие, за которым следует константное выражение определяющее количество битов в поле ;
Struct rgb { unsigned r:3; unsigned g:3; unsigned b:3; };
Примечания
Wikimedia Foundation . 2010 .
Смотреть что такое "Битовое поле" в других словарях:
битовое поле - Смежные биты или октеты в кодовой последовательности, которые декодируются как целое и которые либо представляют абстрактное значение, либо содержат информацию, необходимую для успешного декодирования, либо представляют то и другое. Примечание… …
дополнительное битовое поле - Битовое поле, которое иногда вставляется (для кодирования абстрактного значения), а иногда опускается (МСЭ Т Х.692). Тематики электросвязь, основные понятия EN optional bit field … Справочник технического переводчика
Поле многозначное понятие, связанное с протяжённостью в пространстве: Понятие в математике Поле алгебраическая структура с двумя операциями. Скалярное, векторное и тензорное поля в дифференциальной геометрии (см. вектор и тензор) и матанализе … Википедия
поле маршрутной информации - Битовое поле, содержащее маршрутную информацию. Тематики сети вычислительные EN routing information fieldRIF … Справочник технического переводчика - Битовое поле, которое указывает, присутствует ли дополнительное битовое поле (МСЭ Т Х.692). Тематики электросвязь, основные понятия EN presence determinant … Справочник технического переводчика
детерминант выбора - Битовое поле, которое определяет, какие из нескольких возможных кодовых последовательностей (каждая из которых представляет разные абстрактные значения) имеются в некотором другом битовом поле (МСЭ Т Х.692). : КонстантноеВыражение
Вот такой описатель члена класса и задаёт битовое поле. Битовое поле может существовать исключительно как элемент класса. Идентификатор (необязательный!) задаёт имя поля, константное выражение - размеры этого поля в битах. Согласно ранее приведённым БНФ, подобному описателю должны предшествовать спецификаторы объявления. Как известно, они специфицируют тип объявляемого члена класса.
В C++ существует ограничения на тип битового поля. Это всегда целочисленный тип. Вполне возможно, что тип знаковый. По крайней мере, в Borland C++, максимально допустимый размер поля равняется длине (количеству бит), объекта соответствующего типа.
Рассмотрим пример объявления битового поля:
ОбъявлениеЧленаКласса ::= [СписокСпецификаторовОбъявления ] [СписокОписателейЧленовКласса ]; ::= СпецификаторОбъявления ОписательЧленаКласса ; ::= int [Идентификатор ] : КонстантноеВыражение ; ::= int MyField:5; А вот как объявления битовых полей выглядят в контексте объявления класса: struct BitsFields { int IntField: 1; char CharField: 3; int: 3 unsigned UnsignedField: 1; };Неименованное битовое поле также является членом класса. Существует множество ситуаций, в которых оправдано использование неименованных битовых полей. В конце концов, они ничем не хуже неименованных параметров функций. Неименованные поля могут использоваться для заполнения соответствующей области памяти. Если это поле является полем нулевой длины (для неименованного поля возможно и такое), оно может задавать выравнивание следующего битового поля по границе очередного элемента памяти. Хотя и здесь, в конечном счёте, многое зависит от реализации.
К неименованным битовым полям нельзя обратиться по имени, их невозможно инициализировать и также невозможно прочитать их значение путём непосредственного обращения к битовому полю.
Именованные битовые поля инициализируются подобно обычным переменным. И значения им можно присвоить любые (разумеется, в пределах допустимого для данного типа диапазона значений).
BitsFields QWE; ::::: QWE.CharField = 100; QWE.IntField = 101; QWE.UnsignedField = 1;
Но фактически значения в битовом поле ограничиваются размерами битового поля. Было поле объявлено размером в три бита - диапазон его значений и будет ограничен этими самыми тремя битами:
Cout << QWE.CharField << "....." << endl; cout << QWE.IntField << "....." << endl; cout << QWE.UnsignedField << "....." << endl; :::::
В Borland C++ у битового поля знакового типа, независимо от размеров этого поля, один из битов остаётся знаковым. В результате, однобитовое знаковое поле способно принимать только одно из двух значений: либо -1, либо 0.
В ряде книг утверждается, что битовые поля способствуют "рациональному использованию памяти". В "Справочном руководстве по C++" на этот счёт высказывается мнение, что подобные усилия "наивны и вместо цели (экономии памяти) могут привести к лишним тратам памяти". Даже если в конкретной реализации и удастся упаковать несколько маленьких элементов в одно слово, то извлечение значения битового поля может потребовать дополнительных машинных команд.
Здесь экономия "по мелочам" на деле может обернуться большими потерями. Однако, если битовые поля существуют, значит, кому-то могут быть необходимы или, по крайней мере, удобны.