Знакомство с логическими (побитовыми) операторами
ОГЛАВЛЕНИЕ
Некоторые люди испытывают проблемы с пониманием логических операторов, поэтому была создана данная статья, объясняющая и обучающая их использовать. Знакомство с битами
Что же такое биты?
Простыми словами, биты - это индивидуальные единицы и нули, которые составляют все, что имеет отношение к компьютерам. Все данные, которые вы используете, хранятся в вашем компьютере, используя биты. BYTE (байт) составлен из восьми бит, WORD (слово) равно двум байтам (BYTE), или 16-и битам (Bit). DWORD - это два слова (WORD), или же 32 бита.
Шестнадцатеричные числа и как они связаны с битами
При работе с битами не так легко выразить любое число, используя только единицы и нули, что известно также как двоичное представление. Для этого используются шестнадцатеричные числа.
Как вы уже наверняка знаете, необходимо 4 бита для обозначения чисел от 0 до 15, что также является областью однозначного шестнадцатеричного числа. Данная группа из четырех бит, или пол байта (BYTE), называется полубайт (нибл). Поскольку в одном байте 2 полубайта, мы можем использовать два шестнадцатеричных числа для отображения значения одного байта (BYTE).
Оператор &
Оператор & (И) сравнивает два значения и возвращает значение 1, только в том случае, если оба значения были установлены в 1. Биты сравниваются используя следующую таблицу
Оператор |
Оператор | (ИЛИ) сравнивает два значения и возвращает результат в виде единицы, если хотя бы один из битов будет установлен (равен единице). Биты сравниваются, используя следующую таблицу
Оператор ^
Оператор ^ (XOR) сравнивает два значения и возвращает единицу в случае, если значения сравниваемых элементов различаются. То есть в случае, если сравниваемые значения одинаковы, будет возвращено новое значение. Биты сравниваются, используя следующую таблицу
Оператор ~
Оператор ~ (поразрядное дополнение или обратный код) действует только на одно значение и инвертирует его, преобразуя все единицы в нули, а нули - единицы. Данный оператор используется для установления определенных бит в ноль и обеспечения того, что все другие биты установлены в единицу, независимо от размера данных. Допустим, мы хотим установить все биты в единицу, за исключением нулевого и первого бита:
Операторы >> и <<
Операторы >> (сдвиг вправо) и << (сдвиг влево) передвигают биты на определенное число позиций. Оператор >> сдвигает биты с бита с высоким уровнем к нижнему. Оператор << сдвигает биты со стороны нижнего уровня к биту с верхним уровнем. Одним из вариантов использования является выравнивание битов по какой-либо причине (ознакомьтесь с макросами MAKEWPARAM, HIWORD и LOWORD)
Битовые поля
Другим интересным способом использования битов является создание битовых полей. Имея битовые поля, вы можете установить миниатюрную структуру в пределах байта (BYTE), слова (WORD) или двойного слова (DWORD). К примеру, мы хотим следить за датами, но нам нужно использовать как можно меньше памяти. Мы можем определить следующую структуру
Во-первых, изучим тип данных, который мы используем для структуры битового поля. В данном случае мы использовали байт (BYTE). Байт (BYTE) состоит из 8 бит и, используя его, компилятор распределит один байт памяти. Тем не менее, если мы будем использовать более 8 бит в нашей структуре, то компилятор распределит еще один байт, то есть будет выделено столько байт, сколько необходимо для нашей структуры. Если бы мы использовали WORD или DWORD, то компилятор бы распределил в общем 32 бита для нашей структуры.
Давайте изучим определение различных полей. У нас есть переменные (день, месяц и год), а за ними следуют колонка, которая разделяет переменную от числа бит, которые он содержит. Каждое битовое поле отделяется запятой, и список завершается точкой с запятой.
Теперь у нас есть декларация структуры. Мы располагаем битовые поля в такой структуре, тем самым мы можем использовать стандартную нотацию доступа к структуре для получения членов структуры. Также, поскольку мы не можем получить адреса битовых полей, мы теперь может использовать адрес структуры.
Знакомство с битами
Что же такое биты?Простыми словами, биты - это индивидуальные единицы и нули, которые составляют все, что имеет отношение к компьютерам. Все данные, которые вы используете, хранятся в вашем компьютере, используя биты. BYTE (байт) составлен из восьми бит, WORD (слово) равно двум байтам (BYTE), или 16-и битам (Bit). DWORD - это два слова (WORD), или же 32 бита.
0 1 0 0 0 1 1 1 1 0 0 0 0 1 1 1 0 1 1 1 0 1 0 0 0 1 1 1 1 0 0 0Преимущество наличия логических (побитовых) операторов заключается в том, что вы можете использовать BYTE, WORD или DWORD в качестве маленького массива или структуры. Использование данных операторов поможет вам проверить или установить значения определенных бит или групп бит.
|| | | | ||
|+- 31--ый бит | | | 0-ой бит -+|
| | | | |
+-- BYTE 3 -----+--- BYTE 2 ----+--- BYTE 1 ----+-- BYTE 0 -----+
| | |
+----------- WORD 1 ------------+----------- WORD 0 ------------+
| |
+--------------------------- DWORD -----------------------------+
Шестнадцатеричные числа и как они связаны с битами
При работе с битами не так легко выразить любое число, используя только единицы и нули, что известно также как двоичное представление. Для этого используются шестнадцатеричные числа.Как вы уже наверняка знаете, необходимо 4 бита для обозначения чисел от 0 до 15, что также является областью однозначного шестнадцатеричного числа. Данная группа из четырех бит, или пол байта (BYTE), называется полубайт (нибл). Поскольку в одном байте 2 полубайта, мы можем использовать два шестнадцатеричных числа для отображения значения одного байта (BYTE).
ПОЛУБАЙТ ШЕСТ. ЗНАЧЕНИЕИтак, если бы мы имели один байт (BYTE) содержащий букву 'r' (ASCII код - 114), то он выглядел бы следующим образом:
====== =========
0000 0
0001 1
0010 2
0011 3
0100 4
0101 5
0110 6
0111 7
1000 8
1001 9
1010 A
1011 B
1100 C
1101 D
1110 E
1111 F
0111 0010 Бинарное
7 2 Шестнадцатеричное
Это можно также записать в виде '0x72'
Логические операторы
Существует шесть побитовых оператора:
& оператор И (AND)
| оператор ИЛИ (OR)
^ оператор XOR - исключающее ИЛИ, сложение по модулю 2
~ оператор инверсии
>> оператор сдвига вправо
<< оператор сдвига влево .
Оператор &
Оператор & (И) сравнивает два значения и возвращает значение 1, только в том случае, если оба значения были установлены в 1. Биты сравниваются используя следующую таблицу1 & 1 == 1Данный оператор лучше всего использовать для установки маски проверки значений определенных битов. Допустим, у нас есть байт (BYTE), который содержит некоторые битовые флаги, и мы хотим проверить, был ли четвертый бит установлен в единицу.
1 & 0 == 0
0 & 1 == 0
0 & 0 == 0
BYTE b = 50;В результате будет выполнен следующий подсчет
if ( b & 0x10 )
cout << "Четвертый бит установлен" << endl;
else
cout << "Четвертый бит читс" << endl;
00110010 - bТеперь мы видим, что четвертый бит был установлен.
& 00010000 - & 0x10
----------
00010000 - результат
Оператор |
Оператор | (ИЛИ) сравнивает два значения и возвращает результат в виде единицы, если хотя бы один из битов будет установлен (равен единице). Биты сравниваются, используя следующую таблицу1 | 1 == 1Данный оператор лучше всего использовать для обеспечения установки каких-то определенных битов. Допустим, мы хотим убедиться, что значение третьего бита установлено
1 | 0 == 1
0 | 1 == 1
0 | 0 == 0
BYTE b = 50;В результате будет выполнен следующий подсчет
BYTE c = b | 0x04;
cout << "c = " << c << endl;
00110010 - b
| 00000100 - | 0x04
----------
00110110 - результат
Оператор ^
Оператор ^ (XOR) сравнивает два значения и возвращает единицу в случае, если значения сравниваемых элементов различаются. То есть в случае, если сравниваемые значения одинаковы, будет возвращено новое значение. Биты сравниваются, используя следующую таблицу1 ^ 1 == 0Идеальное использование такого оператора заключается в переключении определенных битов. Допустим, мы хотим изменить третий и четвертый бит
1 ^ 0 == 1
0 ^ 1 == 1
0 ^ 0 == 0
BYTE b = 50;В результате будут выполнены следующие подсчеты
cout << "b = " << b << endl;
b = b ^ 0x18;
cout << "b = " << b << endl;
b = b ^ 0x18;
cout << "b = " << b << endl;
00110010 - b
^ 00011000 - ^ 0x18
----------
00101010 - result
00101010 - b
^ 00011000 - ^ 0x18
----------
00110010 - результат
Оператор ~
Оператор ~ (поразрядное дополнение или обратный код) действует только на одно значение и инвертирует его, преобразуя все единицы в нули, а нули - единицы. Данный оператор используется для установления определенных бит в ноль и обеспечения того, что все другие биты установлены в единицу, независимо от размера данных. Допустим, мы хотим установить все биты в единицу, за исключением нулевого и первого бита:BYTE b = ~0x03;В результате будут выполнены следующие подсчеты
cout << "b = " << b << endl;
WORD w = ~0x03;
cout << "w = " << w << endl;
00000011 - 0x03Другим вариантом использования является комбинация с оператором & для обеспечения того, что определенные биты установлены в нулевое значение. Допустим, мы хотим очистить четвертый бит
11111100 - ~0x03 b
0000000000000011 - 0x03
1111111111111100 - ~0x03 w
BYTE b = 50;В результате будут выполнены следующие подсчеты
cout << "b = " << b << endl;
BYTE c = b & ~0x10;
cout << "c = " << c << endl;
00110010 - b
& 11101111 - ~0x10
----------
00100010 - результат
Операторы >> и <<
Операторы >> (сдвиг вправо) и << (сдвиг влево) передвигают биты на определенное число позиций. Оператор >> сдвигает биты с бита с высоким уровнем к нижнему. Оператор << сдвигает биты со стороны нижнего уровня к биту с верхним уровнем. Одним из вариантов использования является выравнивание битов по какой-либо причине (ознакомьтесь с макросами MAKEWPARAM, HIWORD и LOWORD)BYTE b = 12;В результате будут выполнены следующие подсчеты
cout << "b = " << b << endl;
BYTE c = b << 2;
cout << "c = " << c << endl;
c = b >> 2;
cout << "c = " << c << endl;
00001100 - b
00110000 - b << 2
00000011 - b >> 2
Битовые поля
Другим интересным способом использования битов является создание битовых полей. Имея битовые поля, вы можете установить миниатюрную структуру в пределах байта (BYTE), слова (WORD) или двойного слова (DWORD). К примеру, мы хотим следить за датами, но нам нужно использовать как можно меньше памяти. Мы можем определить следующую структуруstruct date_struct {В данном примере поле, хранящее день, занимает нижние 5 бит, месяц занимает следующие четыре бита, а год - остальные 14 бит. Поэтому мы можем сохранить структуру даты в 23-х битах, что содержится в 3-х байтах. Двадцать четвертый бит игнорируется. Если бы мы определили дату в виде целочисленных значений (integer) для каждого поля, то структура заняла бы 12 байт.
BYTE day : 5, // 1 до 31
month : 4, // 1 до 12
year : 14; // 0 до 9999
} date;
|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|0 0 0 0 0 0 0 0|Теперь давайте разберем данное определение на части и изучим то, что происходит.
| | | |
+------ год ---------------+ месяц +-- день--+
Во-первых, изучим тип данных, который мы используем для структуры битового поля. В данном случае мы использовали байт (BYTE). Байт (BYTE) состоит из 8 бит и, используя его, компилятор распределит один байт памяти. Тем не менее, если мы будем использовать более 8 бит в нашей структуре, то компилятор распределит еще один байт, то есть будет выделено столько байт, сколько необходимо для нашей структуры. Если бы мы использовали WORD или DWORD, то компилятор бы распределил в общем 32 бита для нашей структуры.
Давайте изучим определение различных полей. У нас есть переменные (день, месяц и год), а за ними следуют колонка, которая разделяет переменную от числа бит, которые он содержит. Каждое битовое поле отделяется запятой, и список завершается точкой с запятой.
Теперь у нас есть декларация структуры. Мы располагаем битовые поля в такой структуре, тем самым мы можем использовать стандартную нотацию доступа к структуре для получения членов структуры. Также, поскольку мы не можем получить адреса битовых полей, мы теперь может использовать адрес структуры.
date.day = 12;
dateptr = &date;
dateptr->year = 1852;