Наверное уже ни для кого не секрет, что для компьютера любая информация, в т.ч., и числа, предтставляют из себя набор бит. Например:
1 соответствует 0001 2 - 0010 3 - 0011 4 - 0100 5 - 0101 6 - 0110 7 - 0111 8 - 1000 9 - 1001 10 - 1010 11 - 1011 12 - 1100 и т д
Наиболее распространенными побитовыми операциями в Си-языках являются:
- >> — побитовый сдвиг вправо
- << — побитовый сдвиг влево
&
— ПобитовоеИ
- ^ — исключающее или. Как правило, используется для шифрования. В этой статье рассматриваться не будет
Так же есть | — побитовое ИЛИ, и ~ — побитовое НЕТ
Побитовый сдвиг влево <<
Например рассмотрим число 3, что в двоичной системе = 0011:
3 = 0011 3 << 1 = 0110 = 6 3 << 2 = 1100 = 12 3 << 3 = 1000 = 8
Для непрограммиста можно запомнить так, что y<<x = y * 2^x
, но x
не должна превышать количество байт, отведенное для хранения переменной. При значении x
больше в два раза, чем размер переменной, результатом сдвига влево всегда будет 0
Например, если мы имеем тип WORD
в Си либо short
в шарпе, состоящий из 2-х байт, то:
3 << 1 = 3*2^1 = 3*2 = 6
3 << 2 = 3*2^2 = 12
3 << 3 =
, т.к. 3*2^3= 83
больше 2-х, берем наибольшее число кратное 2^3
в пределах размера переменной
3 << 4 =
, т.к. 3*2^4= 04
больше 2-х в два раза
Побитовый сдвиг вправо >>
Оператор побитового сдвига вправо (>>) сдвигает биты вправо.
12 = 1100 12 >> 1 = 0110 = 6 12 >> 2 = 0011 = 3 12 >> 3 = 0001 = 1
Здесь закономерность обратная:
12 >> 1 = 12*2^-1=12/2=6
12 >> 2 = 12*2^-2=12/4=3
12 >> 3 = 12*2^-3=12/8=
— округляем до меньшего = 11.5
12 >> 3 = 12*2^-3=12/16=
— округляем до меньшего = 00,75
Побитовое И &
По большому счету побитовое И
— это обычное умножение, разве что операции производятся с битами. Как и в любом умножении умножение на 0 дает 0, все остальное — истина. Например:
10 & 1 = 0
11 & 1 = 1
Если разобрать по битам, почему так, то получим:
10 & 1 =1010 & 0001 = (1&0) (0&0) (1&0) (0&1) = 0 0 0 0
11 & 1 =1011 & 0001 = (1&0) (0&0) (1&0) (1&1) = 0 0 0 1
Для чего вообще нужны побитовые операции?
Для чего вообще нужно знание о побитовых операциях?
В языках высокого уровня побитовый сдвиг удобно использовать в enum. Например рассмотрим enum Building
:
[Flags] public enum Building { Administrative = 0x01, //0001 Mercantile = 0x02, //0010 Housing = 0x04, //0100 Industrial = 0x08, //1000 }
Обычный enum — это перечисление (список) констант, которые систематизированы для удобства доступа в одну общую структуру. Если мы объявим обычный список, то переменной типа Building
мы сможем присвоить одно значение.
Но у нас Building
— это здание, а здание может быть не только административным, но и торгово-административным, торгово=промышленным, на первом этаже могут располагаться магазины либо административный центр, а выше — жилые квартиры и т.д. И мы понимаем, что здание может быть многофункциональным. Для такого случая в C# придумали флаги — атрибут позволяющий с помощью побитового сдвига придавать одной переменной несколько значений, например. так:
Building b1 = Building.Administrative | Building.Mercantile | Building.Housing
При этом фактическое значение переменной b1
в битах будет выглядеть так: 0111.
либо одно значение:
Building b1 = Building.Administrative
Тогда фактическое значение переменной b1
в битах будет 0001
, где каждый бит является флагом перечисления Building
.
Побитовые же сдвиги с легкостью позволяют перемещаться между этими константными значениями:
var a1 = Building.Administrative; var m1 = (Building)((int)b1 << 1); Console.WriteLine(c);
Вышеприведенный код вернёт Mercantile
, что соответствует значению 0010
. Соответственно b1 << 2
выдало бы Housing
и так далее.
Так же часто побитовое И
используют для обнуления либо обрезания значения переменной, например:
int PORT = 11; PORT &= 0x01;//1 PORT &= 0x00;//0
На этом пока все. Удачи