# Глава 2.3. Магия побитовых операторов
Побитовые операторы (bitwise operators) нужны для манипуляции _над отдельными битами_ значений. Они применимы только к целым числам.
Начинающие разработчики недооценивают побитовые операторы и побаиваются их. И зря, потому что они повсеместно применяются в реальной практике. Их можно отыскать в любой прикладной области, а не только в сжатии и (де)кодировании данных, работе с аудио/видео, криптографии или разработке микроконтроллеров. Вот популярные, но далеко не единственные сценарии применения битовых операций:
- Быстрая арифметика. В ближайшей [практической работе](/courses/cpp/practice/cpp_div_without_div/) вы сами реализуете деление через побитовый сдвиг.
- Упаковка данных. В _одной_ переменной можно хранить _несколько_ значений, и в этой главе вы узнаете, как.
Кстати, а зачем в одну переменную упаковывать несколько значений? Дело в том, что минимальная адресуемая ячейка памяти — 1 байт. Размер любой переменной кратен байту: нельзя создать переменную размером в 2 бита или 17 бит. Однако нередки ситуации, когда диапазон значений переменной ограничен и известен заранее. Например, целое число, принимающее значения от 0 до 3-х. Для его хранения хватит и 2-х бит, а в один байт поместится аж 4 таких значения! Если состоящих из маленьких значений данных становится много, возникает необходимость экономить память и хранить их _компактно._ Как этого добиться? Для начала вспомним, каким образом числа представлены в памяти.
## Бинарное представление чисел
Каждое число в памяти представлено в виде последовательности бит:
- 5 в бинарном виде — это `0b101`,
- 10 — это `0b1010`,
- 7 — это `0b111`, и так далее.
Префикс `0b` в C++ используется для записи значений в двоичной системе счисления. Если вы плохо с ней знакомы, самое время [восполнить знания.]( https://ru.wikipedia.org/wiki/%D0%94%D0%B2%D0%BE%D0%B8%D1%87%D0%BD%D0%B0%D1%8F_%D1%81%D0%B8%D1%81%D1%82%D0%B5%D0%BC%D0%B0_%D1%81%D1%87%D0%B8%D1%81%D0%BB%D0%B5%D0%BD%D0%B8%D1%8F)
Для удобства длинные числа можно разделять апострофом: `0b111'010`.
Сколько именно бит в памяти занимает число? Это зависит от типа числа и целевой платформы, под которую компилируется код. Например, под значения типа `std::size_t` _чаще всего_ выделяется 4 байта на 32-битных системах и 8 байт на 64-битных.
Так выглядит в памяти число 3 беззнакового типа `std::size_t`, занимающее 4 байта:
```
┌───────────┬──────────┬──────────┬──────────┬──────────┐
│ Значения │ │ │ │ │
│ байтов │ 00000000 │ 00000000 │ 00000000 │ 00000011 │
├───────────┼──────────┼──────────┼──────────┼──────────┤
│ Индексы │ │ │ │ │
│ байтов │ Байт 3 │ Байт 2 │ Байт 1 │ Байт 0 │
└───────────┴──────────┴──────────┴──────────┴──────────┘
```
Обратите внимание, что нумерация байтов идёт справа налево, а не наоборот. Это не ошибка, а договорённость: индекс 0 относится к младшему байту, а максимальный индекс — к старшему байту. Так заведено, чтобы при увеличении разрядности числа не пришлось менять индексы старых байтов. Например, если число будет занимать 8 байтов вместо 4-х, то добавятся старшие байты с индексами 4, 5, 6 и 7. А индексы младших байтов при этом не изменятся.
## Побитовые операторы
Побитовых операторов всего шесть:
- `~a` — побитовое отрицание (инверсия),
- `a & b` — побитовое «И»,
- `a | b` — побитовое «ИЛИ»,
- `a ^ b` — побитовый XOR (взаимоисключающее «ИЛИ»),
- `a << n` — побитовый сдвиг влево: смещение битов числа `a` на `n` позиций влево,
- `a >> n` — побитовый сдвиг вправо: смещение битов числа `a` на `n` позиций вправо.
## Побитовое отрицание: оператор ~
Оператор `~` — это побитовое «НЕ»: `~a` инвертирует биты числа `a`. Нули становятся единицами, а единицы — нулями.
```
┌───┬────┐
│ a │ ~a │
├───┼────┤
│ 0 │ 1 │
├───┼────┤
│ 1 │ 0 │
└───┴────┘
```
Допустим, у нас есть беззнаковое число `x`:
```cpp
std::size_t x = 5;
```
При условии что `std::size_t` занимает 4 байта, в памяти `x` будет представлен следующим образом:
```
┌───────────┬──────────┬──────────┬──────────┬──────────┐
│ Значения │ │ │ │ │
│ байтов │ 00000000 │ 00000000 │ 00000000 │ 00000101 │
├───────────┼──────────┼──────────┼──────────┼──────────┤
│ Нумерация │ │ │ │ │
│ байтов │ Байт 3 │ Байт 2 │ Байт 1 │ Байт 0 │
└───────────┴──────────┴──────────┴──────────┴──────────┘
```
Применим побитовое отрицание:
```cpp
std::size_t y = ~x;
```
В переменную `y` сохранится последовательность бит:
```
┌───────────┬──────────┬──────────┬──────────┬──────────┐
│ Значения │ │ │ │ │
│ байтов │ 11111111 │ 11111111 │ 11111111 │ 11111010 │
├───────────┼──────────┼──────────┼──────────┼──────────┤
│ Нумерация │ │ │ │ │
│ байтов │ Байт 3 │ Байт 2 │ Байт 1 │ Байт 0 │
└───────────┴──────────┴──────────┴──────────┴──────────┘
```
Как видите, это очень большое число. В десятичной системе счисления оно равно `4 145 343 750`. Если бы `std::size_t` занимал 8 байт, а не 4, то число было бы ещё больше.
Перед вами значение типа `std::size_t` размером в 4 байта. Его бинарное представление: `0b11111111'11111111'11111111'11111100`. {.task_text}
Чему будет равно это число после применения к нему оператора `~`? Ответ запишите в десятичной системе счисления. {.task_text}
```consoleoutput {.task_source #cpp_chapter_0023_task_0010}
```
Оператор `~` инвертирует биты числа и в итоге получится бинарное значение `11`. {.task_hint}
```cpp {.task_answer}
3
```
## Побитовое «И»: оператор &
Побитовый оператор `&` похож на логический «И» `&&`, но применяется к числу побитово. Операция `a & b` устанавливает бит результата в 1, если оба соответствующих бита чисел `a` и `b` равны 1:
```
┌───┬───┬───────┐
│ a │ b │ a & b │
├───┼───┼───────┤
│ 0 │ 0 │ 0 │
├───┼───┼───────┤
│ 0 │ 1 │ 0 │
├───┼───┼───────┤
│ 1 │ 0 │ 0 │
├───┼───┼───────┤
│ 1 │ 1 │ 1 │
└───┴───┴───────┘
```
Пример:
```cpp
int x = 5 & 6; // 4
// 0b101 - 5
// & 0b110 - 6
// -----
// 0b100 - 4
```
Чему равен результат выполнения операции `0b11 & 0b1101`? Ответ введите в бинарном формате без префикса `0b`. {.task_text}
```consoleoutput {.task_source #cpp_chapter_0023_task_0020}
```
Добавим в первый операнд два нуля слева: `0b0011 & 0b1101`. {.task_hint}
```cpp {.task_answer}
1
```
Кстати, через побитовое «И» можно проверить, чётное число или нечётное:
```cpp {.example_for_playground .example_for_playground_001}
if ((n & 1) == 1)
{
// n - нечётное
}
```
Разберём, как это работает. У литерала `1` все биты, кроме младшего (нулевого), установлены в `0`. Поэтому у результата операции `n & 1` все биты, кроме нулевого, гарантированно равны 0. А нулевой бит будет равен 1 только в случае, если младший бит числа `n` тоже равен 1. А младший бит числа равен 1 только у нечётных чисел. Это легко заметить: `0b1 == 1`, `0b10 == 2`, `0b11 == 3`, `0b100 == 4` и так далее.
## Побитовое «ИЛИ»: оператор |
Побитовый оператор `|` похож на логический «ИЛИ» `||`, но применяется к числу побитово. Операция `a | b` устанавливает бит результата в 1, если любой из соответствующих битов чисел `a` и `b` равен 1:
```
┌───┬───┬───────┐
│ a │ b │ a | b │
├───┼───┼───────┤
│ 0 │ 0 │ 0 │
├───┼───┼───────┤
│ 0 │ 1 │ 1 │
├───┼───┼───────┤
│ 1 │ 0 │ 1 │
├───┼───┼───────┤
│ 1 │ 1 │ 1 │
└───┴───┴───────┘
```
Пример:
```cpp
int x = 8 | 6; // 14
// 0b1000 - 8
// | 0b0110 - 6
// -----
// 0b1110 - 14
```
Чему равен результат выполнения операции `0b1 | 0b10`? Ответ введите в десятичной системе счисления. {.task_text}
```consoleoutput {.task_source #cpp_chapter_0023_task_0030}
```
Добавим в первый операнд 0 слева: `0b01 & 0b10`. Получим результат `0b11`. {.task_hint}
```cpp {.task_answer}
3
```
## Побитовый XOR: оператор ^ {#block-xor}
У оператора `^` (XOR) нет аналога среди логических операторов. Он устанавливает бит в 1, если соответствующие биты чисел `a` и `b` не равны.
```
┌───┬───┬───────┐
│ a │ b │ a ^ b │
├───┼───┼───────┤
│ 0 │ 0 │ 0 │
├───┼───┼───────┤
│ 0 │ 1 │ 1 │
├───┼───┼───────┤
│ 1 │ 0 │ 1 │
├───┼───┼───────┤
│ 1 │ 1 │ 0 │
└───┴───┴───────┘
```
Пример:
```cpp
int x = 3 ^ 5; // 6
// 0b011 - 3
// ^ 0b101 - 5
// -----
// 0b110 - 6
```
Чему равен результат выполнения операции `0b1011 ^ 0b1011`? Ответ введите в десятичной системе счисления. {.task_text}
```consoleoutput {.task_source #cpp_chapter_0023_task_0040}
```
XOR возвращает 0, если соответствующие биты числа равны. {.task_hint}
```cpp {.task_answer}
0
```
Кстати, через XOR можно проверять числа на равенство:
```cpp {.example_for_playground .example_for_playground_002}
if ((a ^ b) == 0)
{
// a и b равны
}
```
## Побитовый сдвиг влево: оператор << {#block-bitwise}
Оператор `<<` — это **побитовый сдвиг влево.** Запись вида `x << n` означает, что каждый бит числа `x` сдвигается влево на `n` бит. Освобождённые биты заполняются нулями.
Например, если число 3 сдвинуть на 2 бита влево, мы получим 12:
```cpp
3 << 2 // 12
```
Чтобы понять, откуда берётся 12, взглянем на беззнаковое число 3 в двоичном виде:
```
0b00000000'00000000'00000000'00000011
```
```
┌───────────┬──────────┬──────────┬──────────┬──────────┐
│ Значения │ │ │ │ │
│ байтов │ 00000000 │ 00000000 │ 00000000 │ 00000011 │
├───────────┼──────────┼──────────┼──────────┼──────────┤
│ Нумерация │ │ │ │ │
│ байтов │ Байт 3 │ Байт 2 │ Байт 1 │ Байт 0 │
└───────────┴──────────┴──────────┴──────────┴──────────┘
```
Сдвиг `0b11` на 2 бита влево приводит к тому, что биты числа смещаются в сторону старших разрядов, а на их место устанавливаются нули: `0b1100`.
```
┌───────────┬──────────┬──────────┬──────────┬──────────┐
│ Значения │ │ │ │ │
│ байтов │ 00000000 │ 00000000 │ 00000000 │ 00001100 │
├───────────┼──────────┼──────────┼──────────┼──────────┤
│ Нумерация │ │ │ │ │
│ байтов │ Байт 3 │ Байт 2 │ Байт 1 │ Байт 0 │
└───────────┴──────────┴──────────┴──────────┴──────────┘
```
Это и есть 12 в десятичной системе счисления.
Сдвиг числа на один бит влево аналогичен умножению на 2:
```cpp
x << 1 == x * 2
```
Обобщим. Сдвиг числа на `n` бит влево аналогичен умножению на 2 `n` раз, то есть умножению на 2 в степени `n`: `2 ^ n`.
Примеры:
```cpp
4 << 3 // 32
1 << 4 // 16
```
Какое значение сохранится в переменную `x`? {.task_text}
```cpp
int x = 1 << 3;
```
```consoleoutput {.task_source #cpp_chapter_0023_task_0050}
```
Это возведение 2 в степень 3. {.task_hint}
```cpp {.task_answer}
8
```
## Побитовый сдвиг вправо: оператор >>
Оператор `>>` — это **побитовый сдвиг вправо.** Запись `x >> n` означает, что биты числа `x` сдвигаются на `n` бит вправо. Для беззнаковых целых, таких как `std::size_t`, освобождающиеся биты заполняются нулями. Для знаковых целых, таких как `int`, поведение определяется реализацией в компиляторе (implementation-defined).
Как можно догадаться, операция сдвига вправо обратна сдвигу влево. И она аналогична целочисленному делению на `2 ^ n`:
```cpp
9 >> 1 // 4
21 >> 2 // 5
```
Приоритет операторов побитового сдвига ниже, чем у сложения `+` и вычитания `-`. Но выше, чем у операторов сравнения `<`, `>`. Таблицу с приоритетом всех операторов вы можете [посмотреть на cppreference.](https://en.cppreference.com/w/cpp/language/operator_precedence)
Какое значение сохранится в переменную `x`? {.task_text}
```cpp
int x = 16 >> 4;
```
```consoleoutput {.task_source #cpp_chapter_0023_task_0060}
```
Это деление 16 на 2 в степени 4. {.task_hint}
```cpp {.task_answer}
1
```
Напоследок рассмотрим два нюанса работы со сдвигами.
### Отбрасывание битов при сдвиге
При побитовом сдвиге влево лишние биты **отбрасываются.** Допустим, у нас есть большое число:
```
┌───────────┬──────────┬──────────┬──────────┬──────────┐
│ Значения │ │ │ │ │
│ байтов │ 01101000 │ 00000000 │ 00000000 │ 00000000 │
├───────────┼──────────┼──────────┼──────────┼──────────┤
│ Нумерация │ │ │ │ │
│ байтов │ Байт 3 │ Байт 2 │ Байт 1 │ Байт 0 │
└───────────┴──────────┴──────────┴──────────┴──────────┘
```
Если мы сдвинем его на 1 бит влево, оно увеличится:
```
┌───────────┬──────────┬──────────┬──────────┬──────────┐
│ Значения │ │ │ │ │
│ байтов │ 11010000 │ 00000000 │ 00000000 │ 00000000 │
├───────────┼──────────┼──────────┼──────────┼──────────┤
│ Нумерация │ │ │ │ │
│ байтов │ Байт 3 │ Байт 2 │ Байт 1 │ Байт 0 │
└───────────┴──────────┴──────────┴──────────┴──────────┘
```
Но при дальнейшем сдвиге влево старшие разряды потеряются. Они вытеснятся за границы диапазона числа. И вместо того чтобы расти, число наоборот уменьшится:
```
┌───────────┬──────────┬──────────┬──────────┬──────────┐
│ Значения │ │ │ │ │
│ байтов │ 10100000 │ 00000000 │ 00000000 │ 00000000 │
├───────────┼──────────┼──────────┼──────────┼──────────┤
│ Нумерация │ │ │ │ │
│ байтов │ Байт 3 │ Байт 2 │ Байт 1 │ Байт 0 │
└───────────┴──────────┴──────────┴──────────┴──────────┘
```
Если применить к получившемуся числу сдвиг влево ещё на 3 бита, число превратится в 0.
### Побитовый сдвиг литералов
По умолчанию целочисленные литералы имеют тип `int`. И такая запись означает сдвиг значения типа `int` на `n` бит влево:
```
5 << n
```
Однако у типов `int` и `std::size_t` разный диапазон значений. Побитовый сдвиг `int` на слишком большое значение, адекватное для `std::size_t`, приведёт к ошибке в вычислениях. Если вы хотите применить сдвиг именно к значению `std::size_t`, воспользуйтесь суффиксом `uz`: {#block-uz}
```
5uz << n
```
[Суффикс uz](https://en.cppreference.com/w/cpp/language/integer_literal) был добавлен в C++23. Он означает, что у литерала тип `std::size_t`. {#block-suffix-uz}
Сравните результаты сдвига на 30 бит влево для значений типа `int` и `std::size_t`:
```cpp
3 << 30 // -1073741824
3uz << 30 // 3221225472
```
Значение типа `int` при побитовом сдвиге влево стало отрицательным. Это произошло из-за способа представления чисел под названием [«Дополнительный код»](https://ru.wikipedia.org/wiki/%D0%94%D0%BE%D0%BF%D0%BE%D0%BB%D0%BD%D0%B8%D1%82%D0%B5%D0%BB%D1%8C%D0%BD%D1%8B%D0%B9_%D0%BA%D0%BE%D0%B4) (two's complement). Сейчас это не имеет особого значения, но потом пригодится.
## Составное присваивание {#block-compound-assignment}
Побитовые операторы относятся к арифметическим операторам. И для них тоже существуют варианты составного присваивания:
```cpp
x &= 8; // x = x & 8
x |= 2; // x = x | 2
x ^= 4; // x = x ^ 4
x <<= 2; // x = x << 2
x >>= 1; // x = x >> 1
```
## Компактное хранение значений
А теперь перейдём к практическому примеру применения побитовых операторов.
В Unix-системах любому файлу назначаются права. Они определяют, какие с файлом разрешены действия. Допустимо _любое сочетание_ трёх вариантов доступа:
- `r` — чтение,
- `w` — запись,
- `x` — выполнение.
Для хранения этих вариантов требуется ровно 3 бита. Перечислим все возможные комбинации:
```
┌────────────┬──────────┬────────┐
│ Десятичное │ Двоичное │ Доступ │
│ число │ число │ │
├────────────┼──────────┼────────┤
│ 0 │ 000 │ Нет │
├────────────┼──────────┼────────┤
│ 1 │ 001 │ x │
├────────────┼──────────┼────────┤
│ 2 │ 010 │ w │
├────────────┼──────────┼────────┤
│ 3 │ 011 │ wx │
├────────────┼──────────┼────────┤
│ 4 │ 100 │ r │
├────────────┼──────────┼────────┤
│ 5 │ 101 │ rx │
├────────────┼──────────┼────────┤
│ 6 │ 110 │ rw │
├────────────┼──────────┼────────┤
│ 7 │ 111 │ rwx │
└────────────┴──────────┴────────┘
```
Так, двоичная запись `0b100` означает, что файл доступен только для чтения, а `0b011` — что файл можно записывать и исполнять, но нельзя читать.
Права на файл задаются отдельно для:
- `u` — пользователя-владельца,
- `g` — участников группы-владельца,
- `o` — всех остальных.
Значит, чтобы описать весь набор прав на файл, потребуется 9 бит. Например, у владельца могут быть полные права на файл, у группы — на чтение и запись, а у остальных — только на чтение:
```
┌──────────────┬────────┬───────────┐
│ Пользователь │ Группа │ Остальные │
├──────────────┼────────┼───────────┤
│ 0b111 │ 0b110 │ 0b100 │
└──────────────┴────────┴───────────┘
```
Если действовать наивно, то для каждого файла на диске придётся хранить 9 целых чисел. Но можно сэкономить и выделить под права только одно число. Покажем, как это работает, на примере типа `int`. Он гарантированно занимает не меньше 2-х байт, то есть 16-ти бит. А значит, его с запасом хватит для хранения 9 бит, описывающих доступ.
```cpp {.example_for_playground .example_for_playground_003}
const int user = 7; // 0b111
const int group = 6; // 0b110
const int other = 4; // 0b100
int rights = 0;
rights = rights | user; // 0b111
rights = rights << 3; // 0b111'000
rights = rights | group; // 0b111'110
// TODO: добавление прав other
```
Разберём этот код. Мы завели три значения `user`, `group` и `other` и хотим их упаковать в переменную `rights`. Для наглядности мы сначала инициализировали её нулём: все биты числа стали равны нулю. После этого мы применили побитовое «ИЛИ» `rights | user`:
```
0b000 rights
| 0b111 user
-----
0b111
```
Затем мы сдвинули получившееся значение `rights` на 3 бита влево `rights << 3`:
```
ob111 << 3 == 0b111'000
```
Тем самым мы освободили крайние 3 бита под запись нового значения `group`: `rights | group`.
```
0b111'000 rights
| 0b000'110 group
---------
0b111'110
```
Нам осталось по этому же принципу сохранить в переменной `rights` значение `other`.
Вынесем этот пример кода в отдельную функцию `set_rights()`. {.task_text}
Реализуйте её. Для краткости используйте составное присваивание вместо обычных побитовых операций. Например, `a <<= n` вместо `a = a << n`. {.task_text}
```cpp {.task_source #cpp_chapter_0023_task_0070}
int set_rights(int user, int group, int other)
{
int rights = 0;
// Ваш код
return rights;
}
```
Не забудьте добавить упаковку `other`. {.task_hint}
```cpp {.task_answer}
int set_rights(int user, int group, int other)
{
int rights = user;
rights <<= 3;
rights |= group;
rights <<= 3;
rights |= other;
return rights;
}
```
Мы сохранили три числа в одну переменную. Теперь напишем код, который их восстанавливает. Для этого воспользуемся битовой маской.
## Битовые маски
Итак, у нас есть переменная `rights`. Приступим к восстановлению из неё трёх значений: прав пользователя `user`, группы `group` и остальных `other`. Действовать будем в порядке, обратном записи.
Сначала прочитаем `other`. Это последние три бита `rights`. Чтобы получить их значение, применим к `rights` побитовый «И» c числом `0b111`.
```cpp
int rights = 500; // 0b111'110'100
const int other = rights & 0b111;
```
Мы применили к `rights` битовую маску `0b111`.
Битовая маска — это двоичное число для чтения или записи отдельных битов через побитовые операции. Думайте о ней как о трафарете, который накладывается на число, чтобы работать с конкретными битами.
Прочтём значения `group` и `user`. Для этого будем сдвигать `rights` на 3 бита вправо и применять к нему битовую маску.
```cpp {.example_for_playground .example_for_playground_005}
int rights = 500; // 0b111'110'100
const int other = rights & 0b111; // 0b100
rights >>= 3; // 0b111'110
const int group = rights & 0b111; // 0b110
rights >>= 3; // 0b111
const int user = rights & 0b111; // 0b111
```
Решите задачу извлечения трёх чисел из переменной `rights`, не изменяя её. Для этого вам нужно составить три разные битовые маски, применить их к `rights`, а результат операции сдвинуть на нужное количество бит. {.task_text}
Главное — правильно расставьте скобки: приоритет оператора `>>` выше, чем `&`. Найдите в таблице [на cppreference](https://en.cppreference.com/w/cpp/language/operator_precedence) приоритет всех побитовых операторов. {.task_text}
```cpp {.task_source #cpp_chapter_0023_task_0080}
void print_rights(const int rights)
{
// Ваш код
std::println("user={} group={} other={}", user, group, other);
}
```
Чтобы получить значение `user`, нужно оператором `&` применить к `rights` битовую маску `0b111'000'000`, а результат сместить на 6 бит вправо. {.task_hint}
```cpp {.task_answer}
void print_rights(const int rights)
{
const int user = (rights & 0b111'000'000) >> 6;
const int group = (rights & 0b111'000) >> 3;
const int other = rights & 0b111;
std::println("user={} group={} other={}", user, group, other);
}
```
Хранение прав на файл в Unix-стиле — лишь одно из многих применений побитовых операторов для упаковки значений. Другие классические примеры — хранение трёх компонент цвета в модели RGB или 4-х октетов в IPv4-адресе.
## Примеры побитовых операций
Какое значение сохранится в переменную `flags`? Ответ введите в двоичном виде без префикса `0b`. {.task_text}
```cpp {.example_for_playground .example_for_playground_006}
int flags = 0b10101;
flags |= (1 << 3);
```
```consoleoutput {.task_source #cpp_chapter_0023_task_0090}
```
Это установка 3-го бита `flags`. Индексация битов идёт справа налево и начинается с нуля. {.task_hint}
```cpp {.task_answer}
11101
```
Какое значение сохранится в переменную `flags`? Ответ введите в двоичном виде без префикса `0b`. {.task_text}
```cpp {.example_for_playground .example_for_playground_007}
int flags = 0b10101;
flags &= ~(1 << 2);
```
```consoleoutput {.task_source #cpp_chapter_0023_task_0100}
```
Это сброс 2-го бита `flags`. Индексация битов идёт справа налево и начинается с нуля. {.task_hint}
```cpp {.task_answer}
10001
```
Какое значение сохранится в переменную `flags`? Ответ введите в двоичном виде без префикса `0b`. {.task_text}
```cpp {.example_for_playground .example_for_playground_008}
int flags = 0b10101;
flags ^= (1 << 4);
```
```consoleoutput {.task_source #cpp_chapter_0023_task_0110}
```
Это инверсия 4-го бита `flags`. Индексация битов идёт справа налево и начинается с нуля. {.task_hint}
```cpp {.task_answer}
101
```
Напишите функцию, которая проверят, включён ли `n`-ный бит числа `x`. Не забудьте суффикс `uz` для литерала. Индексация битов идёт справа налево и начинается с нуля. {.task_text}
Например, вызов `is_on(0b001, 0)` должен вернуть `true`, а вызов `is_on(0b1011, 2)` — `false` {.task_text}
```cpp {.task_source #cpp_chapter_0023_task_0120}
bool is_on(std::size_t x, std::size_t n)
{
}
```
Примените битовую маску: бит `1`, сдвинутый на `n` позиций влево. Бит установлен в 1, если побитовый «И» числа с этой маской равен `1`.{.task_hint}
```cpp {.task_answer}
bool is_on(std::size_t x, std::size_t n)
{
return x & (1uz << n);
}
```
----------
## Резюме
- Побитовые операторы (bitwise operators) позволяют работать с _отдельными битами_ целых чисел.
- Побитовые операторы:
- `~` — инверсия бита,
- `&` — «И»,
- `|` — «ИЛИ»,
- `^` — XOR,
- `<<` — побитовый сдвиг вправо,
- `>>` — побитовый сдвиг влево.
- Операция `x << n` равносильна умножению `x` на 2 в степени `n`.
- Операция `x >> n` равносильна целочисленному делению `x` на 2 в степени `n`.
- Битовые маски применяются в сочетании с побитовыми операторами для доступа к конкретным битам.
Наша группа в telegram. Здесь можно задавать вопросы и общаться.
Задонатить. Если вам нравится курс, вы можете поддержать развитие площадки!