Урок 45. Побитовые операторы

   ⁄ 

 Обновлено 2 Мар 2017

  ⁄   

Примечание: Некоторые считают этот материал сложным. Если вы застряли или что-то не понятно — пропустите этот урок (и следующий), в будущем сможете вернуться и разобраться детальнее. Он не столь важен для прогресса в изучении C++, как другие уроки, и изложен здесь в большей мере для общего развития.

Побитовые операторы манипулируют отдельными битами в пределах переменной.

Зачем нужны побитовые операторы?

В прошлом, компьютерной памяти было очень мало и ею сильно дорожили. Это было стимулом максимально разумно использовать каждый доступный бит. Рассмотрим тип данных bool – хоть у него есть только два возможных значения (true и false), которые могут быть представлены одним битом, он занимает целый байт памяти! А это, в свою очередь, из-за того, что переменные используют уникальные адреса памяти, а они выделяются только в байтах. Переменная bool занимает 1 бит, а другие 7 тратятся впустую.

Используя побитовые операторы, можно создавать функции, которые позволят уместить 8 значений bool в переменной размером 1 байт, а это уже экономия памяти. В прошлом такой трюк был очень популярен. Но сегодня, по крайней мере, для прикладного программирования, это не так.

Теперь памяти существенно больше и программисты обнаружили, что лучше писать код так, чтобы было проще и понятнее его поддерживать, нежели усложнять ради незначительной экономии. Поэтому спрос на использование побитовых операторов несколько спал, за исключением случаев, когда необходима ну уже максимальная оптимизация (например: научные программы, которые используют огромное количество данных, игры, где манипуляции с битами могут быть использованы для дополнительной скорости, или встроенные программы, где память по-прежнему ограничена).

Есть 6 побитовых операторов:

Оператор Символ Пример Операция
Побитовый сдвиг влево << x << y Все биты в x смещаются влево на y бит
Побитовый сдвиг вправо >> x >> y Все биты в x смещаются вправо на y бит
Побитовое НЕ ~ ~x Все биты в x меняются на противоположные
Побитовое И & x & y Каждый бит в x И каждый бит в y
Побитовое ИЛИ | x | y Каждый бит в x ИЛИ каждый бит в y
Побитовое исключающее ИЛИ (XOR) ^ x ^ y Каждый бит в x XOR каждый бит в y

В битовых операциях следует использовать только целочисленные типы данных unsigned, потому что C++ не всегда гарантирует корректную работу побитовых операторов с целыми числами signed.

Правило: При работе с побитовыми операторами, используйте целочисленные типы данных unsigned.

Побитовый сдвиг влево (<<) и побитовый сдвиг вправо (>>)

Примечание: В следующих примерах мы будем работать с 4-битными двоичными значениями. В C++, количество используемых битов основывается на размере типа данных (в 1 байте 8 бит).

Оператор побитового сдвига влево (<<) сдвигает биты влево. Левый операнд является выражением, в котором они сдвигаются, а правый – количеством битов, которые нужно сдвинуть. Поэтому, в выражении 3 << 1 мы имеем в виду «сдвинуть влево на одно место биты литерала 3».

Рассмотрим число 3, что в двоичной системе = 0011:

3 = 0011
3 << 1 = 0110 = 6
3 << 2 = 1100 = 12
3 << 3 = 1000 = 8

В последнем третьем случае, один бит перемещается за пределы самого литерала! Биты, сдвинутые за пределы двоичного числа, теряются навсегда.

Оператор побитового сдвига вправо (>>) сдвигает биты вправо.

12 = 1100
12 >> 1 = 0110 = 6
12 >> 2 = 0011 = 3
12 >> 3 = 0001 = 1

В третьем случае мы снова переместили бит за пределы литерала. Он также потерялся навсегда.

Хотя в примерах выше мы смещаем литералы, вы также можете смещать и переменные:

Следует помнить, что результаты операций с побитовыми сдвигами зависят от каждого компилятора отдельно.

Что!? Разве операторы << и >> используются не для вывода и ввода данных?

И для этого тоже.

Сейчас польза от использования побитовых операторов не так велика, как было раньше. Сейчас, в большинстве случаев, оператор побитового сдвига влево используется для вывода данных. Рассмотрим следующую программу:

Результат:

8

А как компилятор понимает, когда нужно применять оператор побитового сдвига влево, а когда выводить данные? Всё очень просто. std::cout переопределяет значение << по умолчанию на новое (вывод текста в консоль). Когда он видит, что левым операндом оператора << является std::cout, он знает, что должен быть вывод данных. Если левый операнд — целочисленного типа, то компилятор применяет << как оператор побитового сдвига влево (операция по умолчанию).

Побитовое НЕ

Побитовый оператор НЕ (~), пожалуй, самый простой для объяснения и понимания. Он просто меняет каждый бит на противоположный, например, с 0 на 1 или с 1 на 0. Обратите внимание, результаты побитового НЕ зависят от размера типа данных!

Предположим, размер типа данных — 4 бита:
4 = 0100
~ 4 = 1011 = 11 
(в десятичной системе)

Предположим, 8 бит:
4 = 0000 0100
~ 4 = 1111 1011 = 251
(десятичное)

Побитовые И, ИЛИ и исключающее ИЛИ (XOR)

Побитовые И (&) и ИЛИ (|) работают аналогично логическим И/ИЛИ. Однако, они применяются к каждому биту отдельно! Например, выражение 5 | 6. В двоичной системе, это 0101 | 0110. В любой побитовой операции операнды лучше всего размещать в линию, вот так:

0 1 0 1 // 5
0 1 1 0 // 6

А затем применять операцию к каждому столбцу битов. Как вы помните, логическое ИЛИ возвращает true (1), если один из двух или оба операнды истинны (1), также работает и побитовое ИЛИ. Следовательно, 5 | 6 будет:

0 1 0 1 // 5
0 1 1 0 // 6
-------
0 1 1 1 // 7

Результат — 0111 (7 в десятичной системе).

Также можно обрабатывать комплексные выражения ИЛИ, например, 1 | 4 | 6. Если хоть один бит в столбце равен 1, то результат целого столбца — 1.

0 0 0 1 // 1
0 1 0 0 // 4
0 1 1 0 // 6
--------
0 1 1 1 // 7

1 | 4 | 6 вычисляется в 7.

Побитовое И работает аналогично логическому И: true возвращается, только если оба бита в столбце равны 1. Рассмотрим выражение 5 & 6:

0 1 0 1 // 5
0 1 1 0 // 6
--------
0 1 0 0 // 4

Также можно решать и комплексные выражения И, например, 1 & 3 & 7. Только при условии, что все биты в столбце равны 1, результатом столбца будет 1.

0 0 0 1 // 1
0 0 1 1 // 3
0 1 1 1 // 7
--------
0 0 0 1 // 1

Последний оператор — побитовое исключающее ИЛИ (^) (на англ. — XOR). При обработке двух операндов, исключающее ИЛИ возвращает true (1), только если один и только один из операндов является истинным (1). Если таких нет или все операнды равны 1, то результатом будет false (0). Рассмотрим выражение 6 ^ 3:

0 1 1 0 // 6
0 0 1 1 // 3
-------
0 1 0 1 // 5

Также можно решать и комплексные выражения XOR, например, 1 ^ 3 ^ 7. Если единиц в столбце четное число, то результат — 0. Если нечетное, то результат — 1.

0 0 0 1 // 1
0 0 1 1 // 3
0 1 1 1 // 7
--------
0 1 0 1 // 5

Побитовые операторы присваивания

Как в случае с арифметическими операторами присваивания, C++ предоставляет побитовые операторы присваивания для облегчения внесения изменений в переменные.

Оператор Символ Пример Операция
Присваивание с побитовым сдвигом влево <<= x <<= y Сдвигаем x влево на y бит
Присваивание с побитовым сдвигом вправо >>= x >>= y Сдвигаем x вправо на y бит
Присваивание с побитовой операцией ИЛИ |= x |= y Присваивание x | y для x
Присваивание с побитовой операцией И &= x &= y Присваивание x & y для x
Присваивание с побитовой операцией исключающего ИЛИ ^= x ^= y Присваивание x ^ y для x

Например, вместо х = х << 1; вы можете записать х << = 1;.

Выводы

Подводя итог работы с побитовыми операторами (используя метод столбца) следует помнить:

 при вычислении побитового ИЛИ, если хоть один бит в столбце равен 1, то результат целого столбца — 1;

 при вычислении побитового И, если все биты в столбце равны 1, то результат столбца — 1;

 при вычислении побитового исключающего ИЛИ (XOR), если единиц в столбце нечетное число, то результат — 1.

Тест

1) Каков результат 0110 >> 2 в двоичной системе?

2) Каков результат 5 | 12 в десятичной системе?

3) Каков результат 5 & 12 в десятичной системе?

4) Каков результат 5 ^ 12 в десятичной системе?

Ответы

Ответ 1

0110 >> 2 вычисляется в 0001

Ответ 2

5 | 12 =
0 1 0 1
1 1 0 0
———
1 1 0 1 = 13

Ответ 3

5 & 12 =
0 1 0 1
1 1 0 0
———
0 1 0 0 = 4

Ответ 4

5 ^ 12 =
0 1 0 1
1 1 0 0
———
1 0 0 1 = 9

Оценить статью:

Звёзд: 1Звёзд: 2Звёзд: 3Звёзд: 4Звёзд: 5 (12 оценок, среднее: 4,92 из 5)
Загрузка...
Поделиться в:
Подписаться на обновления:

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *