sonyps4.ru

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 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 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 бит.

Структуры. Перечисления. Объединения. Битовые поля .

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

Извлечение битового поля

Для извлечения битового поля нужно провести две операции:

  1. Умножить операцией логического «И» число на битовую маску - число, у которого в соответствующих разрядах единицы, а в остальных нули.

C = (x & 00001100b) >> 2

  1. Провести побитовый сдвиг вправо.
  2. Умножить операцией логического «И» число на битовую маску соответствующей длины.
c = (x >> 2) & 00000011b

Для младшего поля побитовый сдвиг не нужен, то есть:

A = x & 00000001b

Для старшего поля побитовый сдвиг сам по себе, без умножения на маску, очистит x от ненужных битов - то есть,

D = x >> 4

Замена битового поля

  1. Очистить x от предыдущего значения побитовым умножением на маску с нулями в соответствующих битах.
  2. Побитово сложить 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++" на этот счёт высказывается мнение, что подобные усилия "наивны и вместо цели (экономии памяти) могут привести к лишним тратам памяти". Даже если в конкретной реализации и удастся упаковать несколько маленьких элементов в одно слово, то извлечение значения битового поля может потребовать дополнительных машинных команд.

    Здесь экономия "по мелочам" на деле может обернуться большими потерями. Однако, если битовые поля существуют, значит, кому-то могут быть необходимы или, по крайней мере, удобны.



Загрузка...