C Урок 15. Побитовые операторы. Часть 1



Как мы все знаем, любое число можно привести к двоичному типу. В этом случае один разряд данного числа может принимать только одно из двух значений — 0 или 1, и этот разряд, соответственно, называется битом. До сих пор, изучая язык C, мы работали с числами целиком, но, оказывается, можно оперировать его битами. Не так, конечно, как при использовании ассемблера. Там вообще существуют инструкции, которые изменяют значение отдельно взятого бита. В C таких команд и операций нет, но есть операции, которые работают с каким-то числом и изменяют значение его битов определённым образом и эти операции называются побитовые (или битовые) операции и для реализации данных операций существуют соответствующие операторы, которые, в свою очередь, называются побитовыми (или битовыми) операторами.

Побитовых операторов существует всего шесть. Вот их список:

& — побитовое И (AND).

| — побитовое ИЛИ (OR).

^ — побитовое исключающее ИЛИ (XOR).

<< — сдвиг битов влево.

>> — сдвиг битов вправо.

~ — побитовое отрицание (НЕ или NOT).

Побитовая операция И с применением соответствующего оператора & является бинарной, поэтому работает с двумя операндами.

Данная операция сравнивает биты обоих операндов с одним индексом и в результате мы получаем 1 только в том случае, если оба сравниваемых бита будут равны 1. В остальных случаях результат будет 0.

Для простоты мы не будем брать слишком большие числа, а возьмём числа восьмибитные и далее будем оперировать именно такими значениями, хотя мы можем побитовые операции проводить между значениями любых типов и любых диапазонов.

Произведём нашу операцию, например, между вот такими числами и мы получим в результате вот такое значение

 

 

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

Следующая побитовая операция — это операция ИЛИ, которая происходит благодаря использованию оператора |. В результате данной операции мы, наоборот, получаем ноль только в тех случаях, когда в обоих сравниваемых битах будут нули. Произведём такую операцию между теми же числами

 

 

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

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

 

 

Теперь поговорим о битовых сдвигах. Хотя, здесь и говорить-то, собственно, не о чем. Существует в C всего два типа битовых сдвига — влево << и вправо >>.

 

 

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

Биты в количестве, на которое они сдвигаются, со стороны, противоположной направлению сдвига, заполняются нулями.

Рассмотрим несколько операций сдвига

 

 

 

 

 

Думаю, что с данными операциями также всё понятно.

Остался у нас один оператор, который производит логическое отрицание NOT, обозначаемый тильдой (~). Данный оператор, в отличие от пяти предшествующих, является унарным и операция с помощью него производится только над одним операндом. В результате данной операции все биты инвертируются, то есть единицы превращаются в нули, а нули — в единицы. Данный оператор ставится в коде впереди операнда, над которым он производит операцию побитового инвертирования. Проделаем пару таких операций

 

 

Переходим к практике, которая обещает быть интересной.

 

 

Проект сделаем из проекта MYPROG14 прошлого занятия и имя ему было присвоено MYPROG15.

Откроем файл main.c и в функции main(), как обычно, удалим весь код тела кроме возврата нуля, останется от него вот это

 

int main()

{

   return 0; //Return an integer from a function

}

Удалим также функцию вместе с телом.

Чтобы нам лучше видеть результат наших побитовых операций, нам надо будет написать функцию, которая будет показывать число в консоли в бинарном виде. Заодно и попрактикуемся с написанием кода. Добавим функцию выше функции main(), чтобы не писать прототип. Функция наша пока пустотелая, код тела будем писать постепенно

 

 

Функция ничего не возвращает, в качестве первого аргумента она берёт число целого типа, а в качестве второго — указатель на строковый массив, в котором и будет затем двоичное представление этого числа. Хоть мы и с указателями пока не знакомились, но заранее скажу, что таким образом мы можем возврат значения организовывать прямо во входящем аргументе.

Начнём писать код тела нашей функции.

Объявим для начала строковый массив на 9 символов и обнулим его сразу

 

 

Объявим целых 3 восьмибитные беззнаковые целые переменные

 

 

Также объявим массив на 4 элемента такого же типа, так как максимум у нас будет 4 байта в числе, оно же типа int

 

 

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

 

 

Теперь тело данного цикла. Сначала определимся есть ли у нас ещё байты, не равные нулю. Для этого мы сдвинем наше число влево на количество пунктов равное инкрементируемой переменной, умноженной на 8, тем самым мы будем потихоньку сдвигать данное число на 1 байт влево, пока несдвинутый до сих пор результат не будет равен нулю, либо пока не дойдём до последнего, третьего (байты и биты в числах считаются справа наналево с нуля, а не с единицы) байта. Второе условие само собой сработает по определению цикла типа for. Если условие сработало, то выходим из цикла с помощью оператора break, с которым мы также будем знакомиться несколько позднее. Также проверим, что это не нулевой байт, если байт проверяем нулевой и уже в данном случае результат всего числа будет равен нулю, то мы его все равно будем выводить посимвольно в битах. Таким образом мы не будем выводить лишние старшие байты (вернее их биты)

 

 

Также в данном цикле далее мы присвоим соответствующему элементу массива, объявленного ранее, значение соответствующего байта нашего входного числа

 

 

Выходим из тела цикла и заготовим сначала префикс символьного двоичного представления нашего числа

 

 

Дальше будет ещё цикл, в котором мы уже будем заполнять нашу строку символами нулей и единиц. Добавим его также пока пустотелый

 

 

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

Добавим ещё один цикл, вложенный в наш только что добавленный цикл, пока тоже пустотелый

 

 

Здесь, думаю, вы догадались, что мы будем оперировать уже битами наших преобразуемых в символьный вид байтов.

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

 

 

Несмотря на кажущуюся на первый взгляд сложность выражения оператора switch, сложного здесь ничего нет абсолютно. В данном выражении мы сдвигаем в проверяемом байте с индексом j — k — 1 все биты на столько, чтобы проверяемый бит оказался самым младшим. Затем мы сбрасываем в ноль все остальные биты, применяя операцию И, которую мы только что изучили. В операнде, стоящем после побитового оператора & все биты кроме нулевого равны нулю, поэтому они сбросятся, несмотря на их значение, а самый младший бит, если он равен 1, и в результате будет равен 1, а если 0, то и в результате будет 0, то есть, не изменится. Затем в кейсах мы проверяем, чему равен данный бит, если он равен 0, том мы и записываем символ '0' в соответствующий элемент нашего строкового массива, а если 1 — то записываем символ '1'.

Выходим только из цикла нижнего уровня и добавляем нашу строку к основной строке с помощью соответствующей строковой функции

 

 

Вот и вся функция.

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

 

 

Предыдущий урок Программирование на C Следующая часть

 

 

Смотреть ВИДЕОУРОК (нажмите на картинку)

 

C Побитовые операторы

4 комментария на “C Урок 15. Побитовые операторы. Часть 1
  1. Дмитрий:

    Непонятно что делает strcat(in_str,str_tmp);

  2. Анатолий:

    Подскажите, пожалуйста! Как применять побитовые операции для чисел больше восьми бит?

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *

*