Поразрядные (битовые) операции
Побитовые операции рассматривают операнды как упорядоченные наборы битов,
каждый бит может иметь одно из двух значений - 0 или 1 (наборы двоичных значений). Такие операции позволяют программисту манипулировать значениями отдельных битов. Объект, содержащий набор битов, иногда называют битовым вектором. Он позволяет компактно хранить набор флагов - переменных, принимающих значение "да" "нет".
Операции сдвига - << и >> - бинарные операции. Операнды целого типа. Результат также целого типа. Формат записи:
< Операнд 1 > << < Операнд 2 > -сдвиг влево
< Операнд 1 > >> < Операнд 2 > -сдвиг вправо
Операции выполняют копирование битов двоичного представления первого операнда с сдвигом на количество разрядов, указанное во втором операнду, в соответствующем направлении.
Значение второго операнда должно быть больше или равно 0 и меньше количества двоичных разрядов первого операнда, иначе результат выполнения операций не гарантирован (зависит от реализации, но обычно равен 0).
Примеры:
unsigned a = 20, n = 3, r;
r = a << n;
cout << r << endl; //На экран выведено 160
r = a >> n;
cout << r << endl; //На экран выведено 2
Иллюстрация:
Номер разряда: 31 30 … 8 7 6 5 4 3 2 1 0
Значение a: 0 0 … 0 0 0 0 1 0 1 0 0 = 20
Операция: a << n
Значение r: 0 0 … 0 1 0 1 0 0 0 0 0 = 160
Операция: a >> n
Значение r: 0 0 … 0 0 0 0 0 0 0 1 0 = 2
Операция сдвига влево осуществляет перемещение битов левого операнда a в сторону больших разрядов на количество разрядов, равное значению правого операнда n. Это эквивалентно умножению значения a на 2 в степени n(20 * 8 = 160).
Операция сдвига вправо осуществляет перемещение битов левого операнда a в сторону меньших разрядов на количество разрядов, равное значению правого операнда n. Это эквивалентно делению значения a на 2 в степени n(целочисленное деление 20 / 8 = 2).
Используя операцию сдвига влево очень просто получить любую целую степень двойки в диапазоне степеней равной количеству двоичных разрядов правого операнда без 1. Например, так:
1U << 20 - равно 2 в степени 20, то есть 1048576
При сдвиге влево (в сторону старших разрядов), освобождающиеся младшие разряды замещаются 0 (нулями). При сдвиге вправо возможны две ситуации: если первый операнд беззнаковый (unsigned), то освобождающиеся старшие разряды замещаются 0; если же первый операнд знаковый, то освобождающиеся старшие разряды замещаются либо знаковым разрядом, либо 0 (нет гарантии - зависит от реализации).
Поразрядные логические операции
К этой группе операций относятся:
· ~ - побитовое отрицание (побитовое НЕ) - унарная операция;
· & - побитовая конъюнкция (побитовое И) - бинарная операция;
· | - побитовая дизъюнкция (побитовое ИЛИ) - бинарная операция;
· ^ - побитовое исключающее ИЛИ - бинарная операция.
Операндами этих операций целочисленных типов данных. Результат также целочисленный.
Операция побитовое отрицание (~) осуществляет инвертирование всех байтов двоичного представления своего операнда. Например:
int a = 14, r;
r = ~a;
cout << r << endl; //На экран выведено-15
Иллюстрация:
Номер разряда: 31 30 … 8 7 6 5 4 3 2 1 0
Значение a: 0 0 … 0 0 0 0 0 1 1 1 0 = 14
Значение r = ~a: 1 1 … 1 1 1 1 1 0 0 0 1 = -15
Остальные операции выполняют соответствующую логическую операцию над каждой парой соответствующих разрядов первого и второго операндов, интерпретируя значения двоичных разрядов как логические значения (1 - true; 0 - false). Например:
int a = 14, b = 7, r;
r = a & b;
cout << r << endl; //На экран выведено6
r = a | b;
cout << r << endl; //На экран выведено15
r = a ^ b;
cout << r << endl; //На экран выведено9
Иллюстрация:
Номер разряда: 31 30 … 8 7 6 5 4 3 2 1 0
Значение a: 0 0 … 0 0 0 0 0 1 1 1 0 = 14
Значение b: 0 0 … 0 0 0 0 0 0 1 1 1 = 7
Операция: a & b
Значение r: 0 0 … 0 0 0 0 0 0 1 1 0 = 6
Операция: a | b
Значение r: 0 0 … 0 0 0 0 0 1 1 1 1 = 15
Операция: a ^ b
Значение r: 0 0 … 0 0 0 0 0 1 0 0 1 = 9
Использование побитовых операций
Как уже говорилось, побитовые операции используются для обработки отдельных двоичных разрядов памяти. Для манипулирования отдельным битом необходимо научиться делать следующее:
· определять значение заданного бита;
· устанавливать значение заданного бита в значение 0 или 1;
· инвертировать значение заданного бита.
Это можно сделать так:
unsigned a = 1234; // Целое значение, битами которого мы будем управлять
unsigned short n = 4;// Номер необходимого бита (от 0 до 31)
bool r; // Значение результата (0 или 1)
/* Узнаем, чему равен n-й бит (двоичный разряд) значения a. Результат поместим в переменную r */
r = a & (1U << n);
cout << "Разряд с номером " << n << " равен " << r << endl; //значение1
/* Установим n-й бит (двоичный разряд) значения aв0. Результат поместим в переменную а */
a = a & (~ (1U << n));
cout << "Значение а равно " << a << endl; //значение1218
/* Проверяем */
r = a & (1U << n);
cout << "Разряд с номером " << n << " равен " << r << endl; //значение0
/* Возвращаем n-й бит (двоичный разряд) значения a в 1. Результат поместим в переменную а*/
a = a | (1U << n);
cout << "Значение а равно " << a << endl; //значение1234
/* Проверяем */
r = a & (1U << n);
cout << "Разряд с номером " << n << " равен " << r << endl; //значение1
/* Инвертируем n-й бит (двоичный разряд) значения a. Результат поместим в переменную а */
a = a ^ (1U << n);
cout << "Значение а равно " << a << endl; //значение1218
/* Проверяем */
r = a & (1U << n);
cout << "Разряд с номером " << n << " равен " << r << endl; //значение0
/* Еще раз инвертируем n-й бит (двоичный разряд) значения a. Результат поместим в переменную а */
a = a ^ (1U << n);
cout << "Значение а равно " << a << endl; //значение1234
/* Проверяем */
r = a & (1U << n);
cout << "Разряд с номером " << n << " равен " << r << endl; //значение1
Изменяя значение переменной n в диапазоне от 0 до 31 можно выполнить все эти действия над любым битом переменной a какое бы значение она не содержала.
Таким образом, для того, чтобы узнать, чему равен двоичный разряд с номером n в значении переменной a, мы воспользовались выражением
a & (1U << n).
Иллюстрация вычисления этого выражения:
Номер разряда: 31 30 … 11 10 9 8 7 6 5 4 3 2 1 0
1U: 0 0 … 0 0 0 0 0 0 0 0 0 0 0 1 = 1
1U << n: 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0
Значение a: 0 0 … 0 1 0 0 1 1 0 1 0 0 1 0 = 1234
a & (1U << n): 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0 = 16
Результатом вычисления этого выражения является целое значение не равное 0. Операция присваивания этого значения логической переменной r автоматически преобразует целое значение 16 в логическое значение true (т.е. 1).
Если бы значение a имело бы разряд с номером 4 равным 0, то результатом вычисления этого выражения было бы значение 0. При выполнении операции присваивания это значение было бы преобразовано в логическое значение false (т.е. 0).
Для установки значения разряда с номером n в переменной a в значение 0 используется выражение
a & (~ (1U << n)).
Иллюстрация вычисления этого выражения:
Номер разряда: 31 30 … 11 10 9 8 7 6 5 4 3 2 1 0
1U: 0 0 … 0 0 0 0 0 0 0 0 0 0 0 1 = 1
1U << n: 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0
~ (1U << n): 1 1 … 1 1 1 1 1 1 1 0 1 1 1 1
Значение a: 0 0 … 0 1 0 0 1 1 0 1 0 0 1 0 = 1234
a & (1U << n): 0 0 … 0 1 0 0 1 1 0 0 0 0 1 0 = 1218
Для установки значения разряда с номером n в переменной a в значение 1 используется выражение
a | (1U << n).
Иллюстрация вычисления этого выражения:
Номер разряда: 31 30 … 11 10 9 8 7 6 5 4 3 2 1 0
1U: 0 0 … 0 0 0 0 0 0 0 0 0 0 0 1 = 1
1U << n: 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0
Значение a: 0 0 … 0 1 0 0 1 1 0 0 0 0 1 0 = 1218
a | (1U << n): 0 0 … 0 1 0 0 1 1 0 1 0 0 1 0 = 1234
Для инвертирования значения разряда с номером n в переменной a используется выражение
a ^ (1U << n).
Иллюстрация вычисления этого выражения при a = 1218:
Номер разряда: 31 30 … 11 10 9 8 7 6 5 4 3 2 1 0
1U: 0 0 … 0 0 0 0 0 0 0 0 0 0 0 1 = 1
1U << n: 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0
Значение a: 0 0 … 0 1 0 0 1 1 0 0 0 0 1 0 = 1218
a ^ (1U << n): 0 0 … 0 1 0 0 1 1 0 1 0 0 1 0 = 1234
Но, если a = 1234, то:
Номер разряда: 31 30 … 11 10 9 8 7 6 5 4 3 2 1 0
1U: 0 0 … 0 0 0 0 0 0 0 0 0 0 0 1 = 1
1U << n: 0 0 … 0 0 0 0 0 0 0 1 0 0 0 0
Значение a: 0 0 … 0 1 0 0 1 1 0 1 0 0 1 0 = 1234
a ^ (1U << n): 0 0 … 0 1 0 0 1 1 0 0 0 0 1 0 = 1218
В C++ имеются и другие средства работы с отдельными битами, но они будут рассмотрены позже.
Дата добавления: 2019-02-07; просмотров: 338;