В предыдущих частях мы познакомились с битовыми операциями инверсии, сдвига, и "и". Теперь посмотрим на две оставшиеся.
| - "или"
Данная операция работает схоже с "и", но по другим правилам:
1 | 1 = 1
1 | 0 = 1
0 | 1 = 1
0 | 0 = 0
Если у операции "и" результат был 1 только тогда, когда обе части выражения равны 1, то у "или" результат равен 1, когда или одна часть равна 1, или другая часть равна 1, или обе они равны 1. Поэтому операция называется "или".
Чем она полезна? Она почти всегда следует рука об руку с операцией "и", работая с ней в паре и делая в большинстве случаев обратные действия. Там, где "и" разрушает, "или" создает. Например, операция "и" может обнулить выбранный бит. А операция "или" может его установить в 1. Вспомним пример про статус игрока.
Чтобы "включить" состояние отравления, нужно сделать операцию "или" со статусом и маской отравления 00000010 (2):
status = status | 2
Чтобы упаковать несколько битовых масок вместе, также можно использовать "или":
mask = 2 | 8
Чтобы этим методом можно было нормально пользоваться, заведите константу для каждой маски и тогда у вас получится что-то вроде
mask = POISON | BOOST
Собственно, на этом всё, так как основная часть была описана ранее про операцию "и".
Осталась одна довольно странная операция:
^ - "исключающее или", она же "xor"
1 ^ 1 = 0
0 ^ 1 = 1
1 ^ 0 = 1
0 ^ 0 = 0
Она дает результат 1, когда первая и вторая часть выражения отличаются, то есть действует как "или", но исключает случаи, когда обе части равны. Потому и "исключающее или".
Трудно сходу представить, какая от неё может быть польза. Эта операция используется в алгоритмах с сильным математическим уклоном, так что ожидать от неё каких-то простых и понятных применений не стоит. Есть, однако, парочка лайфхаков:
1. Обнуление
a ^ a = 0
Любое число, вступив в "xor" с самим собой, превратится 0. Потому что 1 получается только тогда, когда две части операции различны, но число не может быть отлично само от себя. Значит, везде получатся нули. Вы скажете – нафига так делать, если можно просто написать a = 0? Сейчас так делать уже не стоит. Корни этого метода уходят далеко в прошлое, в программы на языке ассемблера. Там операция xor ax, ax выполнялась быстрее, чем mov ax, 0. Вот и вся причина.
Просто будьте в курсе. Может быть, когда-нибудь пригодится.
2. Обмен значениями
Нередко возникает задача: поменять местами значения переменных a и b. Если мы напишем a = b, то a станет равно b. Но написать b = a уже нельзя – значение a, которое было, потерялось. Стандартно эта проблема решается через временную переменную:
tmp = a
a = b
b = tmp
Для этого требуется создать лишнюю переменную. Но можно обойтись теми, что есть:
a = a ^ b
b = b ^ a
a = a ^ b
Выглядит как очень сильное колдунство. Давайте рассмотрим детально.
a = 1010 (10)
b = 0011 (3)
После первой операции a ^ b в переменной a остаются включенными только те биты, которые не совпадают с b: 1001.
После второй операции b ^ a в переменной b остаются включенными только те биты, которые не совпадают с текущим a: 1010, то есть b становится равным оригинальному a.
После третьей операции a ^ b в переменной a остаются включенными только те биты, которые не совпадают с b (то есть на этот раз с оригинальным a): 0011.
Теперь a = 3, b = 10.
Честно говоря, всё равно выглядит как колдунство. Можно медитировать до полного понимания, или просто запомнить :)