Цвет рамки желтый.
Комбинация битов 00000000000000000001011100001100
В коде есть несколько моментов, которые необходимо обсудить. Одно из отличий между этими двумя представлениями состоит в том, что побитовому представлению нужна информация о позициях. Например, для представления синего цвета в программе используется константа BLUE, имеющая числовое значение 4. Но поскольку данные организованы в структуре, на самом деле хранить настройку синего цвета для фона будет бит 3 (не забывайте, что нумерация начинается с нуля (см. рис. 15.1)), а настройку синего цвета для рамки — бит 11. Таким образом, в программе определен ряд новых констант:
#define FILL_BLUE 0x8
#define BORDER_BLUE 0x800
Здесь 0x8 — это значение, когда в 1 установлен только бит 3, а 0x800 — значение, когда в 1 установлен только бит 11. Первую константу можно применять при установке бита синего цвета для фона окна, а вторую — при установке бита синего цвета для рамки. Шестнадцатеричная запись упрощает выяснение, какие биты задействованы. Вспомните, что каждая шестнадцатеричная цифра представляет четыре бита. Следовательно, значение 0x800 соответствует комбинации битов 0x8, но с дописанными восемью битами с состоянием 0. Глядя на десятичные эквиваленты, 2048 и 8, заметить такую связь гораздо труднее.
Если значения являются степенями 2, можно воспользоваться операцией сдвига влево. Например, последние две директивы #define можно заменить следующим образом:
#define FILL_BLUE 1<<3
#def ine BORDER_BLUE 1<<11
Во втором операнде указана степень для возведения числа 2. Так, значение 0x8 равно 2 s, а значение 0x800 — 2 И. Аналогично, выражение 1<<<11 являются константными и вычисляются на этапе компиляции.
Вместо директивы #define для создания символических констант можно применять перечисление.
652 Глава 15
Например, можно поступить так:
enum { OPAQUE = 0xl, FILL_BLUE = 0x8, FILL_GREEN = 0x4, FILL_RED = 0x2, FILL_MASK = 0xE, BORDER = 0x100, BORDER_BLUE = 0x800,
BORDER_GREEN = 0x400, BORDER_RED = 0x200, BORDER_MASK = 0xEOO,
B_DOTTED = 0x1000, B_DASHED = 0x2000, STYLE_MASK = 0x3000 };
Если вы не намерены создавать переменные с типом этого перечисления, указывать имя в объявлении не обязательно.
Обратите внимание, что использовать побитовые операции для изменения настроек сложнее. В качестве примера давайте установим голубой цвет для фона окна. В данном случае недостаточно просто включить биты, соответствующие синему и зеленому цветам:
box.us_view |= (FILL_BLUE | FILL_GREEN); /* сбросить фон */
Дело в том, что цвет также полагается на настройку бита, отвечающего за красный цвет. Если этот бит был включен ранее (скажем, для получения желтого цвета), то приведенный код оставит бит красного цвета установленным и установит в 1 биты синего и зеленого цветов, давая в результате белый цвет. Обойти эту проблему проще всего, сначала отключив все биты, отвечающие за цвет, и лишь затем устанавливать новые значения. Именно поэтому в программе содержится следующий код:
box.us_view &= ~FILL_MASK; /* очистить биты фона */
box.us_view |= (FILL_BLUE | FILL_GREEN); /* переустановить фон */
Для демонстрации того, что может произойти, если предварительно не очистить соответствующие биты, в программе также предусмотрена такая строка:
box.us_view |= BORDER_RED; /* ошибочный подход */
Из-за того, что бит BORDER_GREEN уже был установлен, результирующим цветом будет BORDER_GREEN | BORDER_RED, что соответствует желтому цвету.
В си туациях подобного рода применять битовые поля проще:
box . st view.hll_color = CYAN; /* эквивалент с битовым полем */
Тогда нет нужды в предварительной очистке битов. Кроме того, члены битовых полей допускают использование одних и тех же значений цвета для рамки и фона окна. В случае подхода с побитовыми операциями придется применять отличающиеся значения (значения, отражающие действительные позиции битов).
Далее, сравните следующие два оператора вывода:
printf("Цвет рамки %s.\n", colors[pb->border_color]);
printf("Цвет рамки %s.\n", colors!(us >> 9) & 07]);
В первом операторе выражение pb->border_color имеет значение из диапазона 0-7, поэтому его можно использовать как индекс в массиве colors. Получить ту же информацию с помощью побитовых операций сложнее. Один из подходов предусматривает применение ui >> 9 для сдвига битов цвета рамки в самую правую позицию (биты 0-2) с последующим объединением полученного значения с маской 07, в результате чего все биты кроме трех самых правых будут отключены. То, что осталось, будет находиться в диапазоне 0-7 и может использоваться в качестве индекса для массива colors.
Манипулирование битами 653
Внимание!
Соответствие между битовыми полями и позициями битов зависит от реализации. Например, при выполнении программы из листинга 15.4 в старой системе Macintosh PowerPC получается следующий вывод:
Читать дальше