Поразрядные операции
Что такое поразрядные операции
Поразрядные операции работают с отдельными битами числа. Число в памяти представлено в двоичной системе (0 и 1).
int number = 5; // В двоичном виде: 00000101
Двоичная система
- Каждая позиция — это степень двойки: 2⁰, 2¹, 2², 2³...
- Число 5 = 4 + 1 = 2² + 2⁰ = 101₂
Основные поразрядные операторы
| Оператор | Название | Описание | Пример |
|---|---|---|---|
& | И (AND) | 1 только если оба бита равны 1 | 5 & 3 = 1 |
| | ИЛИ (OR) | 1 если хотя бы один бит равен 1 | 5 | 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;
}
| A | B | A & B |
|---|---|---|
| 0 | 0 | 0 |
| 0 | 1 | 0 |
| 1 | 0 | 0 |
| 1 | 1 | 1 |
Пошаговое вычисление:
101 (5)
& 011 (3)
-----
001 (1)
Поразрядное ИЛИ (|)
Результат равен 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)
// Сдвиг влево равен умножению на степень 2
int n = 7;
printf("%d << 1 = %d (то же что %d * 2 = %d)\n",
n, n << 1, n, n * 2);
printf("%d << 2 = %d (то же что %d * 4 = %d)\n",
n, n << 2, n, n * 4);
printf("%d << 3 = %d (то же что %d * 8 = %d)\n",
n, n << 3, n, n * 8);
Сдвиг вправо (>>)
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
- Флаги: компактное хранение булевых значений
- Маски: выделение определенных битов
- Криптография: многие алгоритмы используют поразрядные операции
Поразрядные операции — мощный инструмент для эффективной работы с данными на низком уровне.