Битовые операции

Скалярными типами данных называются все типы, принимающие целочисленные значения: char, short int, int, long int, long long, а также их signed и unsigned модификации.

Для хранения каждого из этих типов в памяти отводится определенное количество байт. Для того, чтобы узнать размер памяти, отводимый для хранения той или иной переменной можно использовать оператор sizeof: например, sizeof(int) возвращает количество байт, необходимых для хранения переменной типа int, а sizeof(A), где A — идентификатор переменной, возвращает количество байт, необходимой для хранения переменной A.

Каждую переменную скалярного типа будем представлять в виде последовательности бит, нумеруя их от 0, биты будем записывать справа налево (то есть бит с номером 0 будет записан самым правым, а самый старший бит — самым левым).

Например, если переменная a объявлена, как unsigned char, то ее можно записать в виде последовательности из 8 бит или двух шестнадцатеричных цифр.

unsigned char a;
a = 0    ; // 00000000 или 0x00
a = 1    ; // 00000001 или 0x01
a = 2    ; // 00000010 или 0x02
a = 10   ; // 00001010 или 0x0A
a = 255  ; // 11111111 или 0xFF

Например, если a=10, то в битовой записи a биты с номерами 1 и 3 равны 1, а остальные биты равны 0.

Для двух переменных одинакового скалярного типа определены битовые операции:
& битовое И (AND)
| битовое ИЛИ (OR)
^ битовое ИСКЛЮЧАЮЩЕЕ ИЛИ (XOR)
~ битовое ОТРИЦАНИЕ (NOT) — унарная операция.

Битовые операторы работают следующим образом. Берутся два операнда, и к каждой паре соответствующих бит для левого и правого операнда применяется данная операция, результатом будет переменная того же типа, каждый бит которой есть результат применения соответствующей логической операции к соответствующим битам двух операндов. Рассмотрим пример:

unsigned char a, b, c, d, e, f;
a = 5     ; // 00000101
b = 6     ; // 00000110

c = a & b ; // 00000100 == 4
d = a | b ; // 00000111 == 7
e = a ^ b ; // 00000011 == 3
f =  ~ a  ; // 11111010 == 250

Битовое отрицание числа (величина f в последнем примере) — это число, полученное из исходного заменой всех нулей на единицы и наоборот.

Будьте аккуратны, не путайте логические и битовые операции. Например, 2 && 1 == 1, поскольку применение логического “И” к двум значениям 2 и 1, то есть к двум “истинам”, это истина, но 2 & 1 == 0!

Есть еще две операции, работающие с битами: это битовые сдвиги. Их два: сдвиг влево и вправо. Оператор a >> n возвращает число, которое получается из a сдвигом всех бит на n позиций вправо, при этом самые правые n бит отбрасываются. Например:

unsigned char a, b, c, d, e;
a = 43      ; // 00101011
b = a >> 1  ; // 00010101 == 21
c = a >> 2  ; // 00001010 == 10
d = a >> 3  ; // 00000101 == 5
e = a >> 5  ; // 00000001 == 1

Понятно, что для положительных чисел битовый сдвиг числа вправо на n равносилен целочисленному делению на 2n.

Аналогично, битовый сдвиг влево на n бит равносилен (для положительных чисел) умножению на 2n и осуществляется при помощи оператора <<:

unsigned char a;
a = 5       ; // 00000101
b = a << 1  ; // 00001010 == 10
c = a << 2  ; // 00010100 == 20
d = 2 << 3  ; // 00101000 == 40