IT Notes

Битовые флаги как аргументы функций на C/C++

Введение

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

Постановка задачи

Предположим, нам нужна функция для управления светодиодными индикаторами клавиатуры (Scroll Lock, Num Lock и Caps Lock). Чтобы не усложнять приложение, реализуем его схематично. Будем выводить на консоль состояние, соответствующее переданным битовых флагам. Для этого используем следующую функцию:

void printLEDState( bool on ) {
    if( on ) {
        std::cout << " * ";
    } else {
        std::cout << " _ ";
    }
}

Если индикатор включен, то на консоли отобразится символ *, иначе _.

Применение битовых флагов

Первым делом определим следующее перечисление:

enum KeyboardLED {
    NONE        = 0,                                    // 0000
    NUM_LOCK    = 1,                                    // 0001
    CAPS_LOCK   = 1 << 1,                               // 0010
    SCROLL_LOCK = 1 << 2,                               // 0100
    ALL         = NUM_LOCK | CAPS_LOCK | SCROLL_LOCK    // 0111
};

Рядом с каждым значением в виде комментария мы указали его двоичное представление. Обратите внимание, что значения NUM_LOCK, CAPS_LOCK и SCROLL_LOCK выбраны таким образом, что единицы в их бинарном представлении не накладываются. Для определения этих значений мы использовали оператор бинарного сдвига << на один бит влево. Это означает, что бинарное умножение любого из них даст в результате ноль, то есть они в совокупности образуют набор битовых флагов.

Еще один интересный прием, который мы здесь наблюдаем, заключается в использовании комбинированного флага ALL. Он представляет собой побитовое сложение | всех возможных битовых флагов.

Реализуем саму функцию для установки состояния индикаторов:

void setKeyboardLEDState( unsigned int state ) {
    printLEDState( state & NUM_LOCK );
    printLEDState( state & CAPS_LOCK );
    printLEDState( state & SCROLL_LOCK );
    std::cout << std::endl;
}

На вход она принимает параметр state. Проверка того, должен ли быть включен каждый индикатор или нет, осуществляется с помощью оператора побитового умножения &. Например, для CAPS_LOCK мы видим:

state & CAPS_LOCK

Если биты в state и CAPS_LOCK наложатся, то в функцию printLEDState() будет передана истина, иначе ложь.

Рассмотрим пример, чтобы лучше понять, как это работает. Передадим функции setKeyboardLEDState() параметр state, равный NUM_LOCK | CAPS_LOCK (то есть 0001 | 0010 = 0011). Во время проверок state & NUM_LOCK (0011 & 0001 = 0001) и state & CAPS_LOCK (0011 & 0010 = 0010) мы видим, что единицы в бинарном представлении накладываются и получаются ненулевые значения, которые соответствуют true. Однако в последней проверке state & SCROLL_LOCK (0011 & 0100 = 0000) получается ноль, который соответствует false. В результате на экран будет выведено: * * _.

А вот еще несколько примеров запуска с указанием результатов, которые будут выведены на консоль:

setKeyboardLEDState( NONE );                 // _ _ _
setKeyboardLEDState( NUM_LOCK | CAPS_LOCK ); // * * _
setKeyboardLEDState( ALL );                  // * * *

Заключение

Использование битовых флагов - довольно удобный и одновременно мощный прием. Его сильной чертой является возможность комбинирования различных режимов работы. Но прежде, чем использовать описанный метод, изучите следующую заметку: Указатели на функции в C++. В ней продемонстрирован более гибкий подход к параметризации поведения функций, который может подойти для решения вашей задачи еще лучше.

Понравилась статья?
Не забудь поделиться ей с друзьями!

Похожие публикации

Комментарии

Спасибо за статью, было полезно освежить в памяти.

RSS RSS-рассылка

Популярное

Дешевый хостинг