red = color & BYTE_MASK;
green = (color >> 8) & BYTE_MASK;
blue = (color >> 16) & BYTE_MASK;
В коде посредством операции сдвига вправо 8-битовое значение составляющей цвета перемещается в младший байт. Затем с помощью приема с маской значение младшего байга присваивается желаемой переменной.
Манипулирование битами 639
пример программы
В главе 9 при написании программы преобразования чисел в двоичное представление мы использовали рекурсию. Теперь мы решим ту же задачу с применением побитовых операций. Программа в листинге 15.1 читает вводимое с клавиатуры целое число и передает его вместе с адресом строки в функцию по имени itobs(), которая строит для целочисленного значения строку с двоичным представлением. Для определения подходящей комбинации 0 и 1, помещаемой в строку, эта функция использует побитовые операции.
Листинг 15.1. Программа binbi t.с

640 глава 15
В листинге 15.1 применяется макрос CHAR BIT из заголовочного файла limits.h. Этот макрос представляет количество битов в типе char. Операция sizeof возвращает размер в терминах char, поэтому выражение CHAR_BIT * sizeof (int) дает количество битов в значении int. Массив bin_str содержит на один элемент больше этой величины, чтобы можно было добавить в него завершающий нулевой символ.
Функция itobs() возвращает тот же самый адрес, который ей был передан, так что ее вызов можно использовать, к примеру, в качестве аргумента printf(). На первой итерации цикла for функция вычисляет выражение 01 & n. Операнд 01 — это восьмеричное представление маски, у которой все биты кроме нулевого установлены в 0. Следовательно, результатом 01 & n будет значение последнего бита в n. Значением является 0 или 1, но для массива необходим символ ‘0’ или символ ‘1 '. Преобразование осуществляется добавлением кода для '04 (Это предполагает, что цифры кодируются последовательно, как в ASCII.) Результат помещается в предпоследний элемент массива. (Последний элемент зарезервирован для нулевого символа.)
Кстати, вместо выражения 01 & n можно применить и 1 & n. Использование восьмеричного значения 1 вместо десятичного выглядит более стильно. С этой точки зрения вариант 0x1 & n, пожалуй, даже лучше.
Затем в цикле выполняются операторы i— и n >>= 1. Первый оператор приводит к переходу на предыдущий элемент массива, а второй сдвигает биты в n на одну позицию вправо. На следующей итерации цикла код найдет значение нового самого правого бита. После этого соответствующий ему символ цифры помещается в элемент, предшествующий последней цифре. В подобной манере функция заполняет массив справа налево.
Для отображения результирующей строки можно применять printf() или puts(). Тем не менее, в листинге 15.1 определена функция show_bstr(), которая разбивает последовательность битов на группы по четыре, чтобы облегчить восприятие строки.
Ниже приведен пример выполнения программы:
Вводите целые числа и просматривайте их двоичные представления.
Нечисловой ввод завершает программу.
7
7 представляется как 0000 0000 0000 0000 0000 0000 0000 0111
2013
2013 представляется как 0000 0000 0000 0000 0000 0111 1101 1101
-1
-1 is 1111 1111 1111 1111 1111 1111 1111 1111
32123
32123 представляется как 0000 0000 0000 0000 0111 1101 0111 1011
q
Программа завершена.
Еще один пример
Давайте рассмотрим еще один пример. На этот раз цель заключается в том, чтобы написать функцию, которая инвертирует последние n битов в значении, принимая в качестве аргументов n и само значение.
Операция ~ инвертирует биты, но делает это со всеми битами в байте, а не только с избранными. Однако, как вы уже видели, для переключения отдельных битов можно использовать операцию ^(исключающее “ИЛИ”). Предположим, что создана маска, в которой последние n битов установлены в 1, а остальные — в 0. Тогда применение ^к этой маске и значению переключает, или инвертирует, последние n битов, оставляя остальные биты без изменений. Такой подход реализован в следующем фрагменте кода:
Манипулирование битами 641
int invert_end(int num, int bits)
{
int mask = 0; int bitval = 1;
while (bits-- > 0)
{
mask |= bitval; bitval <<= 1;
}
return num ^mask;
}
Маска создается в цикле while. Изначально в mask все биты установлены в 0. На первой итерации цикла бит 0 устанавливается в 1, после чего значение bitval увеличивается до 2, т.е. в нем бит 0 устанавливается в 0, а бит 1 — в 1. На следующей итерации бит 1 в mask устанавливается в 1 и т.д. В конце концов, операция num ^mask дает желаемый результат.
Читать дальше