Операторы побитового выражения в C

Привет, будущие маги кодирования! Сегодня мы отправляемся в захватывающее путешествие по миру побитовых операторов в C. Не волнуйтесь, если вы новичок в программировании; я стану вашим дружелюбным гидом и объясню все шаг за шагом. Так что наденьте свои шапки для обучения и погружайтесь вместе с нами!

C - Bitwise Operators

Что такое побитовые операторы?

Прежде чем начать, давайте поймем, что такое побитовые операторы. Представьте себе, что у вас есть несколько выключателей. Побитовые операторы — это как специальные инструменты, которые позволяют вам управлять этими выключателями интересными способами — включать и выключать их или даже менять их состояния. В мире компьютеров эти "выключатели" на самом деле являются битами (0 и 1), из которых состоит наша data.

Теперь давайте рассмотрим каждый побитовый оператор по порядку.

Побитовый оператор И (&) в C

Побитовый оператор И похож на избирательного друга, который говорит "да" только когда оба входа согласны. Он сравнивает каждый бит двух чисел и возвращает 1 только если оба бита равны 1. В противном случае он возвращает 0.

Посмотрим на пример:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 в двоичной системе
unsigned int b = 13;  // 13 = 0000 1101 в двоичной системе

printf("a & b = %d\n", a & b);

return 0;
}

Вывод:

a & b = 12

Что здесь происходит? Разберем это:

0011 1100  (60 в двоичной системе)
& 0000 1101  (13 в двоичной системе)
----------
0000 1100  (12 в десятичной системе)

Видите, как он сохраняет только 1, где у обоих чисел есть 1? Вот в чем магия побитового И!

Побитовый оператор ИЛИ (|)

Побитовый оператор ИЛИ похож на щедрого друга, который говорит "да", если хотя бы один вход согласен. Он возвращает 1, если хотя бы один из соответствующих битов равен 1.

Вот пример:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 в двоичной системе
unsigned int b = 13;  // 13 = 0000 1101 в двоичной системе

printf("a | b = %d\n", a | b);

return 0;
}

Вывод:

a | b = 61

Разберем это:

0011 1100  (60 в двоичной системе)
| 0000 1101  (13 в двоичной системе)
----------
0011 1101  (61 в десятичной системе)

Видите, как он сохраняет 1 где угодно, где у числа есть 1? Вот это побитовое ИЛИ!

Побитовый оператор Исключающее ИЛИ (^)

Оператор Исключающее ИЛИ похож на странного друга, который любит, чтобы вещи были разными. Он возвращает 1, если биты различны, и 0, если они одинаковы.

Посмотрим на это в действии:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 в двоичной системе
unsigned int b = 13;  // 13 = 0000 1101 в двоичной системе

printf("a ^ b = %d\n", a ^ b);

return 0;
}

Вывод:

a ^ b = 49

Вот что происходит:

0011 1100  (60 в двоичной системе)
^ 0000 1101  (13 в двоичной системе)
----------
0011 0001  (49 в десятичной системе)

Исключающее ИЛИ часто используется в криптографии, потому что его легко обратить. Если вы Исключающее ИЛИ дважды с одним и тем же значением, вы получите обратное число. Круто, правда?

Оператор Сдвига влево (<<)

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

Вот как это работает:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 в двоичной системе

printf("a << 2 = %d\n", a << 2);

return 0;
}

Вывод:

a << 2 = 240

Разберем это:

0011 1100  (60 в двоичной системе)
<<2 (сдвиг влево на 2)
----------
1111 0000  (240 в десятичной системе)

Заметите, как биты переместились влево, а новые 0 заполнили справа? Также сдвиг влево на 1 равен умножению на 2. Так что сдвиг влево на 2 равен умножению на 4!

Оператор Сдвига вправо (>>)

Оператор сдвига вправо — это как противоположный близнец сдвига влево. Он перемещает биты вправо.

Посмотрим на пример:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 в двоичной системе

printf("a >> 2 = %d\n", a >> 2);

return 0;
}

Вывод:

a >> 2 = 15

Вот что происходит:

0011 1100  (60 в двоичной системе)
>>2 (сдвиг вправо на 2)
----------
0000 1111  (15 в десятичной системе)

Биты переместились вправо, а новые 0 заполнили слева. Сдвиг вправо на 1 равен делению на 2 (ignoring remainders).

Оператор Дополнения до 1 (~)

Оператор дополнения до 1 похож на зеркало для битов. Он переворачивает каждый бит из 0 в 1 и наоборот.

Вот как это работает:

#include <stdio.h>

int main() {
unsigned int a = 60;  // 60 = 0011 1100 в двоичной системе

printf("~a = %u\n", ~a);

return 0;
}

Вывод:

~a = 4294967235

Что здесь произошло? Разберем это:

0000 0000 0000 0000 0000 0000 0011 1100  (60 в 32-битной двоичной системе)
~(дополнение до 1)
----------------------------------------
1111 1111 1111 1111 1111 1111 1100 0011  (4294967235 в десятичной системе)

Каждый 0 стал 1, а каждый 1 стал 0. Результат выглядит большим, потому что мы используем unsigned int, который интерпретирует все эти 1 как положительное число.

Заключение

Итак, это было, друзья! Мы совершили путешествие по земле побитовых операторов в C. Эти мощные инструменты могут показаться немного сложными с первого взгляда, но с практикой вы обнаружите, что они чрезвычайно полезны для задач, таких как работа с оборудованием, оптимизация кода или даже некоторые крутые трюки для вечеринок (если ваши вечеринки включают бинарную математику, конечно).

Помните, ключ к владению этими операторами — это практика. Попробуйте написать свои собственные программы с использованием этих операторов. Экспериментируйте с различными числами и видите, какие результаты вы получите. И до того, как вы это поймете, вы будете мастерами битового манипулирования!

Счастливого кодирования, и да будут биты всегда в вашей милости!

Оператор Символ Описание
И & Возвращает 1, если оба бита равны 1, в противном случае 0
ИЛИ | Возвращает 1, если хотя бы один бит равен 1, в противном случае 0
Исключающее ИЛИ ^ Возвращает 1, если биты различны, 0 если одинаковы
Сдвиг влево << Сдвигает биты влево на заданное количество позиций
Сдвиг вправо >> Сдвигает биты вправо на заданное количество позиций
Дополнение до 1 ~ Переворачивает все биты (0 становится 1, 1 становится 0)

Credits: Image by storyset