Перейти к основному содержимому

Поразрядные операции

Что такое поразрядные операции

Поразрядные операции работают с отдельными битами числа. Число в памяти представлено в двоичной системе (0 и 1).

int number = 5;  // В двоичном виде: 00000101
Двоичная система
  • Каждая позиция — это степень двойки: 2⁰, 2¹, 2², 2³...
  • Число 5 = 4 + 1 = 2² + 2⁰ = 101₂

Основные поразрядные операторы

ОператорНазваниеОписаниеПример
&И (AND)1 только если оба бита равны 15 & 3 = 1
|ИЛИ (OR)1 если хотя бы один бит равен 15 | 3 = 7
^Исключающее ИЛИ (XOR)1 если биты разные5 ^ 3 = 6
~НЕ (NOT)Инвертирует все биты~5 = -6
<<Сдвиг влевоСдвигает биты влево5 << 1 = 10
>>Сдвиг вправоСдвигает биты вправо5 >> 1 = 2

Поразрядное И (&)

Результат равен 1, только если оба бита равны 1.

#include <stdio.h>

int main() {
int a = 5; // 101 в двоичном виде
int b = 3; // 011 в двоичном виде
int result = a & b;

printf("a = %d (двоичное: 101)\n", a);
printf("b = %d (двоичное: 011)\n", b);
printf("a & b = %d (двоичное: 001)\n", result);

return 0;
}

Поразрядное ИЛИ (|)

Результат равен 1, если хотя бы один бит равен 1.

int a = 5;  // 101
int b = 3; // 011
int result = a | b; // 111 = 7

printf("5 | 3 = %d\n", result); // 7

Вычисление:

  101  (5)
| 011 (3)
-----
111 (7)

Исключающее ИЛИ (^)

Результат равен 1, если биты разные.

int a = 5;  // 101
int b = 3; // 011
int result = a ^ b; // 110 = 6

printf("5 ^ 3 = %d\n", result); // 6

Вычисление:

  101  (5)
^ 011 (3)
-----
110 (6)

Поразрядное НЕ (~)

Инвертирует все биты числа.

int a = 5;        // 00000101
int result = ~a; // 11111010 = -6

printf("~5 = %d\n", result); // -6
Важно

Результат ~ зависит от размера типа данных и представления отрицательных чисел в системе.

Операции сдвига

Сдвиг влево

int number = 5;  // 101

printf("5 << 1 = %d\n", number << 1); // 10 (1010)
printf("5 << 2 = %d\n", number << 2); // 20 (10100)
printf("5 << 3 = %d\n", number << 3); // 40 (101000)

Визуализация:

Исходное: 00000101 (5)
<< 1: 00001010 (10)
<< 2: 00010100 (20)
<< 3: 00101000 (40)

Сдвиг вправо (>>)

int number = 20;  // 10100

printf("20 >> 1 = %d\n", number >> 1); // 10 (1010)
printf("20 >> 2 = %d\n", number >> 2); // 5 (101)
printf("20 >> 3 = %d\n", number >> 3); // 2 (10)

Визуализация:

Исходное: 00010100 (20)
>> 1: 00001010 (10)
>> 2: 00000101 (5)
>> 3: 00000010 (2)

Практические применения

Проверка четности числа

#include <stdio.h>

int main() {
int numbers[] = {4, 7, 12, 15, 20};
int size = 5;

for (int i = 0; i < size; i++) {
// Младший бит четного числа всегда 0
if (numbers[i] & 1) {
printf("%d - нечетное\n", numbers[i]);
} else {
printf("%d - четное\n", numbers[i]);
}
}

return 0;
}

Быстрое умножение и деление на степени 2

int multiply_by_8(int x) {
return x << 3; // Умножение на 8 (2³)
}

int divide_by_4(int x) {
return x >> 2; // Деление на 4 (2²)
}

int main() {
int number = 12;

printf("%d * 8 = %d\n", number, multiply_by_8(number));
printf("%d / 4 = %d\n", number, divide_by_4(number));

return 0;
}

Работа с флагами

#include <stdio.h>

// Определяем флаги
#define READ_PERMISSION 1 // 001
#define WRITE_PERMISSION 2 // 010
#define EXECUTE_PERMISSION 4 // 100

int main() {
int permissions = 0;

// Устанавливаем права на чтение и запись
permissions |= READ_PERMISSION;
permissions |= WRITE_PERMISSION;

printf("Текущие права: %d\n", permissions);

// Проверяем наличие прав
if (permissions & READ_PERMISSION) {
printf("Есть право на чтение\n");
}

if (permissions & WRITE_PERMISSION) {
printf("Есть право на запись\n");
}

if (permissions & EXECUTE_PERMISSION) {
printf("Есть право на выполнение\n");
} else {
printf("Нет права на выполнение\n");
}

// Убираем право на запись
permissions &= ~WRITE_PERMISSION;
printf("После удаления права на запись: %d\n", permissions);

return 0;
}

Полезные приемы

Переключение бита

int toggleBit(int number, int position) {
return number ^ (1 << position);
}

int main() {
int num = 5; // 101
printf("Исходное число: %d\n", num);

num = toggleBit(num, 1); // Переключаем 1-й бит
printf("После переключения 1-го бита: %d\n", num); // 111 = 7

return 0;
}

Подсчет установленных битов

int countBits(int number) {
int count = 0;
while (number) {
count += number & 1; // Добавляем младший бит
number >>= 1; // Сдвигаем вправо
}
return count;
}

int main() {
int num = 13; // 1101 - три единицы
printf("Количество единиц в числе %d: %d\n", num, countBits(num));

return 0;
}

Частые ошибки

Типичные проблемы
// ❌ Путаница логических и поразрядных операций
if (flags & PERMISSION && other_condition) { // Правильно
// НЕ:
if (flags && PERMISSION & other_condition) { // Неправильный приоритет

// ❌ Сдвиг на отрицательное число
int result = 5 << -1; // Неопределенное поведение!

// ❌ Сдвиг больше размера типа
int result = 5 << 32; // Для 32-битного int - проблема
Когда использовать
  • Оптимизация: быстрые операции умножения/деления на степени 2
  • Флаги: компактное хранение булевых значений
  • Маски: выделение определенных битов
  • Криптография: многие алгоритмы используют поразрядные операции

Поразрядные операции — мощный инструмент для эффективной работы с данными на низком уровне.