Побитовые операции
Краткое описание
В этом уроке знакомимся с побитовыми операциями в Go — нишевым инструментом для работы с двоичным представлением чисел. Разбираем операции AND, OR, XOR и битовые сдвиги. Автор честно предупреждает: в реальной разработке это используется редко, но знать о существовании нужно.
Ключевые концепции
Двоичное представление чисел
Любое десятичное число можно представить в двоичном формате:
5 (десятичное) = 0101 (двоичное)
3 (десятичное) = 0011 (двоичное)
Все побитовые операции работают с двоичным представлением.
Побитовое И (AND) — оператор &
Правило: результат 1 только если оба бита равны 1, иначе 0.
1 & 1 = 1
1 & 0 = 0
0 & 1 = 0
0 & 0 = 0
Пример:
result := 5 & 3
// 0101 (5)
// 0011 (3)
// ----
// 0001 (1)
fmt.Println(result) // 1
Применение: маскирование битов, проверка флагов.
Побитовое ИЛИ (OR) — оператор |
Правило: результат 0 только если оба бита равны 0, иначе 1.
0 | 0 = 0
0 | 1 = 1
1 | 0 = 1
1 | 1 = 1
Пример:
result := 5 | 3
// 0101 (5)
// 0011 (3)
// ----
// 0111 (7)
fmt.Println(result) // 7
Применение: установка флагов, объединение битовых масок.
Побитовое исключающее ИЛИ (XOR) — оператор ^
Правило: результат 1 если биты разные, 0 если одинаковые.
0 ^ 0 = 0
1 ^ 1 = 0
0 ^ 1 = 1
1 ^ 0 = 1
Пример:
result := 5 ^ 3
// 0101 (5)
// 0011 (3)
// ----
// 0110 (6)
fmt.Println(result) // 6
Применение: переключение битов, простое шифрование, поиск уникальных элементов.
Побитовый сдвиг влево («) — умножение на 2
Суть: все биты сдвигаются влево, справа добавляются нули.
result := 5 << 1
// 0101 (5) << 1
// 1010 (10)
fmt.Println(result) // 10
Математика: x << n = x * 2^n
5 << 1 // 5 * 2¹ = 10
5 << 2 // 5 * 2² = 20
5 << 3 // 5 * 2³ = 40
Применение: быстрое умножение на степени двойки.
Побитовый сдвиг вправо (») — деление на 2
Суть: все биты сдвигаются вправо, младший бит отбрасывается.
result := 5 >> 1
// 0101 (5) >> 1
// 0010 (2)
fmt.Println(result) // 2
Математика: x >> n = x / 2^n (целочисленное деление)
128 >> 1 // 128 / 2¹ = 64
128 >> 2 // 128 / 2² = 32
128 >> 3 // 128 / 2³ = 16
Применение: быстрое деление на степени двойки.
Практические примеры
Базовые побитовые операции
package main
import "fmt"
func main() {
var a int = 5 // 0101
var b int = 3 // 0011
// Побитовое И
fmt.Printf("%d & %d = %d\n", a, b, a&b) // 5 & 3 = 1
// Побитовое ИЛИ
fmt.Printf("%d | %d = %d\n", a, b, a|b) // 5 | 3 = 7
// Побитовое XOR
fmt.Printf("%d ^ %d = %d\n", a, b, a^b) // 5 ^ 3 = 6
}
Побитовые сдвиги
value := 5 // 0101
// Сдвиг влево (умножение на 2)
fmt.Printf("%d << 1 = %d\n", value, value<<1) // 5 << 1 = 10
fmt.Printf("%d << 2 = %d\n", value, value<<2) // 5 << 2 = 20
fmt.Printf("%d << 3 = %d\n", value, value<<3) // 5 << 3 = 40
// Сдвиг вправо (деление на 2)
value = 128
fmt.Printf("%d >> 1 = %d\n", value, value>>1) // 128 >> 1 = 64
fmt.Printf("%d >> 2 = %d\n", value, value>>2) // 128 >> 2 = 32
Составные побитовые операторы
x := 5
x &= 3 // x = x & 3 → 1
x |= 7 // x = x | 7 → 7
x ^= 2 // x = x ^ 2 → 5
x <<= 1 // x = x << 1 → 10
x >>= 2 // x = x >> 2 → 2
Важные моменты
1. Редко используется на практике
Автор честно предупреждает: в большинстве backend-задач (работа с JSON, API, базы данных) побитовые операции почти не нужны.
2. Работает одинаково во всех языках
Побитовые операции — это фундамент двоичной математики. Работают идентично в Go, C#, Java, JavaScript и других языках.
3. Не тратьте много времени
Достаточно знать, что они существуют. При необходимости всегда можно вернуться и изучить подробнее.
4. Применение в нишевых задачах
- Работа с правами доступа (флаги)
- Оптимизация памяти (битовые маски)
- Криптография
- Сетевые протоколы
- Низкоуровневое программирование
5. Сдвиги быстрее умножения/деления
На уровне процессора сдвиг выполняется за одну инструкцию, что быстрее умножения/деления. Но современные компиляторы сами оптимизируют x * 2 в x << 1.
6. Одинаковые на всех архитектурах
Побитовые операции работают одинаково на современных процессорах (x86, ARM). Исключение — квантовые компьютеры, но это другая история.
Best Practices
1. Используйте для флагов и прав
const (
FlagA = 1 << 0 // 1
FlagB = 1 << 1 // 2
FlagC = 1 << 2 // 4
)
flags := FlagA | FlagC // установить A и C
if flags&FlagA != 0 { // проверить A
// флаг установлен
}
2. Комментируйте побитовый код
// Маскируем младшие 8 бит
value &= 0xFF
// Устанавливаем 5-й бит
value |= (1 << 5)
3. Не оптимизируйте преждевременно
// Не нужно писать так ради "оптимизации"
result := x << 3 // x * 8
// Пишите понятно, компилятор сам оптимизирует
result := x * 8
4. Используйте константы для читаемости
// Плохо
permissions := 7 // что это значит?
// Хорошо
const (
Read = 1 << 0
Write = 1 << 1
Execute = 1 << 2
)
permissions := Read | Write | Execute // понятно!
Что запомнить
- Побитовые операции работают с двоичным представлением чисел
&(AND):1только если оба бита1|(OR):0только если оба бита0^(XOR):1если биты разные<<(сдвиг влево): умножение на2^n>>(сдвиг вправо): деление на2^n- Составные операторы:
&=,|=,^=,<<=,>>= - Редко используется в обычной разработке
- Основное применение: флаги, права доступа, битовые маски
- Работает одинаково во всех языках программирования
- Не нужно глубоко изучать — достаточно знать о существовании
- При необходимости можно вернуться и изучить детально
- Не стоит переживать, если тема показалась сложной