Перейти к содержимому
Теория · Операторы и выражения · урок 5 из 8

Побитовые операторы, тернарный оператор и приоритет

Настройки вроде «жирный», «курсив» и «подчёркнутый» принимают значения «да» или «нет». Их удобно хранить в одном целом числе, по одному разряду на каждую. Число в памяти — ряд двоичных разрядов, битов, и каждый равен 0 или 1. Особые операторы читают и меняют отдельные биты:

C++
#include <iostream>

int main() {
    unsigned a = 0b1100u;
    unsigned b = 0b1010u;
    std::cout << (a & b) << '\n';  // 8
    std::cout << (a | b) << '\n';  // 14
    std::cout << (a ^ b) << '\n';  // 6
    std::cout << (a & ~b) << '\n';  // 4
    std::cout << (a >> 2) << '\n';  // 3
    std::cout << (1u << 3) << '\n';  // 8
}

Литерал с префиксом 0b записывает число в двоичном виде. Биты нумеруют справа налево с нуля, и каждый следующий бит стоит вдвое больше предыдущего:

  • бит 0 — 1;
  • бит 1 — 2;
  • бит 2 — 4;
  • бит 3 — 8.

Число равно сумме битов, равных 1: 0b1100u — это 8 + 4, то есть 12, а 0b1010u — это 8 + 2, то есть 10.

Побитовые операторы

Побитовые операторы работают с каждым битом отдельно, а &, | и ^ сравнивают биты с одинаковым номером в двух числах:

  • & (И) даёт 1, если бит равен 1 в обоих числах: a & b — это 0b1000u;
  • | (ИЛИ) даёт 1, если бит равен 1 хотя бы в одном числе: a | b — это 0b1110u;
  • ^ (исключающее ИЛИ) даёт 1, если в двух числах бит разный: a ^ b — это 0b0110u;
  • ~ (НЕ) переворачивает все биты, поэтому a & ~b оставляет в a только биты, которых нет в b;
  • << и >> сдвигают биты влево и вправо: a >> 2 — это 0b0011u.

1u << n — число, в котором установлен только бит номер n. Сдвиг на столько битов, сколько их в типе, или больше — неопределённое поведение: для 4-байтового unsigned это сдвиг на 32 и больше.

Такое число называют маской. С ней flags | (1u << n) ставит бит номер n, flags & ~(1u << n) снимает его, а flags & (1u << n) проверяет. Выберите операцию и посмотрите, как маска ставит, снимает или проверяет бит номер n, не трогая остальные.

ВживуюМаска: поставить, снять, проверить бит(flags & mask) != 0 → true
unsigned flags = 0b00000110u;  // 6
unsigned mask = 1u << 2;       // 4
flags4 + 2 = 6

Младшие 8 битов 32-битного unsigned: старшие 24 бита у flags, mask и результата — нули

От flags остался только бит 2: результат 4, поэтому (flags & mask) != 0 — true.

Нажимайте на биты flags и меняйте n

Побитовые операторы применяют к беззнаковым типам. У знаковых типов старший бит означает знак числа, и его называют битом знака. ~ или << могут изменить этот бит, и число станет отрицательным.

Тернарный оператор

Тернарный оператор c ? a : b выбирает одно из двух значений. Если условие c верно, результат — a, иначе — b, и вычисляется только выбранный операнд:

C++
int x = 7;
int y = 4;
int bigger = x > y ? x : y;
std::cout << bigger << '\n';  // 7
std::cout << (x % 2 == 0 ? "even" : "odd") << '\n';  // odd

Приоритет

Приоритет операторов задаёт порядок действий в выражении: как в математике, * выполняется раньше +. Основные операторы — от высокого приоритета к низкому:

  • !, ~, ++, -- и унарный -;
  • *, /, %, затем + и -, затем << и >>;
  • <, <=, >, >=, затем == и !=;
  • &, затем ^, затем |;
  • &&, затем ||, затем ?: и =.

Отсюда две ловушки:

  • сдвиг выполняется после сложения, поэтому 1 << n + 1 — это 1 << (n + 1);
  • ?: стоит почти в самом низу, поэтому в c ? a : b + 1 единица прибавляется только к b.

У std::cout оператор << тот же, и приоритет у него тот же. Поэтому в примерах выше a & b и ?: стоят в скобках: без них первым сгруппировался бы std::cout << a, и строка не скомпилировалась бы. Если порядок вызывает сомнение, ставьте скобки.

Частая ошибка

Бит проверяют сравнением без скобок:

C++
#include <iostream>

int main() {
    unsigned flags = 0b0110u;
    bool has_bit_2 = flags & 4u == 4u;  // ✗
    std::cout << has_bit_2 << '\n';  // 0
}
TEXT
warning: & has lower precedence than ==; == will be evaluated first [-Wparentheses]

Компилятор только предупреждает. == выполняется раньше &: 4u == 4u даёт true, то есть 1, и flags & 1u равно 0, хотя бит номер 2 установлен. Нужны скобки: (flags & 4u) == 4u.

Главное

  • Число равно сумме своих битов, равных 1, а бит номер n стоит 1u << n.
  • | с маской ставит бит, & ~ снимает его, а & проверяет.
  • c ? a : b вычисляет только выбранный операнд.
  • &, ^ и | слабее ==, поэтому проверку бита берут в скобки.

Подпишись, чтобы пройти курс

Курс бесплатный. Войди через Telegram и подпишись на канал — откроются все уроки.

Войти другим способом