Краткий обзор того, что такое Bitflags
Битовые флаги — это константы, определяющие некий набор, обычно опций различных типов. Битовые флаги обычно определяются как шестнадцатеричные константы, и цель состоит в том, чтобы использовать побитовые операторы с этими константами, чтобы создать некоторое подмножество из общего набора констант с помощью побитовых операторов.
Побитовые операторы — это такие операторы, как |
(побитовое ИЛИ), &
(побитовое И), ^
(побитовое исключающее ИЛИ) и ~
(побитовое НЕ), которые выполняют побитовую логическую операцию, указанную оператором над двумя значениями. для создания нового значения. Побитовые операторы отличаются от логических операторов, таких как ||
(логическое ИЛИ), &&
(логическое И), которые используются с выражениями, результатом которых является логическое значение true (не ноль) или false (ноль).
Примером типичного определения, использующего директивы препроцессора C define
для создания побитовых флагов, может быть:
#define ITM_FLAG_EXAMPLE1 0x00000001L // an example bitwise flag
#define ITM_FLAG_EXAMPLE2 0x00000002L // another example flag
#define ITM_FLAG_JUMP01 0x00040000L // another example
#define ITM_FLAG_JUMP02 0x00080000L // another example
Кроме того, современные компиляторы C также позволяют использовать типы enum
.
typedef enum { ITEM_FLAG_1 = 1, ITEM_FLAG_2 = 2, ITEM_FLAG_3 = 4 } ItemType;
ItemType AnItem = ITEM_FLAG_1; // defining a variable of the type
ItemType AnItem2 = ITEM_FLAG_1 | ITEM_FLAG_2; // defining a second variable
or
enum { ITEM_FLAG_1 = 1, ITEM_FLAG_2 = 2, ITEM_FLAG_3 = 4 } ItemType;
enum ItemType AnItem = ITEM_FLAG_1; // defining a variable of the type
enum ItemType AnItem2 = ITEM_FLAG_1 | ITEM_FLAG_2; // defining a second variable
И несколько примеров того, как их можно использовать:
unsigned long ulExample = ITM_FLAG_EXAMPLE2; // ulExample contains 0x00000002L
unsigned long ulExamplex = ITM_FLAG_EXAMPLE1 | ITM_FLAG_EXAMPLE2; // ulExamplex contains 0x00000003L
unsigned long ulExampley = ulExamplex & ITM_FLAG_EXAMPLE2; // ulExampley contains 0x00000002L
См. эту публикацию в блоге, Введение в таблицы истинности и логическую алгебру, в котором описаны различные операции логической алгебры, и этот тема Википедии о таблицах истинности.
Некоторые рекомендации по использованию битовых флагов
При использовании битовых флагов может быть несколько ошибок и несколько вещей, на которые следует обратить внимание.
- использование константы, определенной битовым флагом, которая равна нулю, биты не установлены, может вызвать проблемы
- побитовые операции, смешанные с логическими операциями, могут быть областью дефектов
В общем, использование битового флага, определение которого равно нулю, а не нулю, может привести к ошибкам. Большинство программистов ожидают, что битовый флаг будет иметь ненулевое значение, которое представляет принадлежность к некоторому набору. Битовый флаг, определение которого равно нулю, является своего рода нарушением ожиданий и при использовании в побитовых операциях может привести к неожиданным последствиям и поведению.
При комбинировании побитовых операций с битовыми переменными вместе с логическими операторами круглые скобки для обеспечения приоритета конкретных операций обычно более понятны, поскольку читателю не требуется знать таблица приоритетов операторов C.
К опубликованным вопросам
Когда предоставляется библиотека, обычно имеется один или несколько включаемых файлов, которые сопровождают файл библиотеки, чтобы предоставить ряд необходимых элементов.
- прототипы функций для функций, предоставляемых библиотекой
- объявления типов переменных и определения для типов, используемых библиотекой
- специальные константы для операндов и флаги, управляющие поведением библиотечных функций
Битовые флаги — проверенный временем способ предоставления опций для функционального интерфейса. Битовые флаги имеют несколько замечательных свойств, которые делают их привлекательными для программиста на C.
- компактное представление, которое легко передать через интерфейс
- естественно подходит для операций булевой алгебры и манипулирования множествами
- естественное соответствие побитовым операторам C для выполнения этих операций
Битовые флаги экономят место, потому что имя флага может быть длинным и описательным, но компилируется в одно значение без знака, такое как unsigned short или unsigned long или unsigned char.
Другое приятное свойство использования битовых флагов заключается в том, что при использовании побитовых операций над константами в выражении большинство современных компиляторов оценивают побитовую операцию как часть компиляции выражения. Таким образом, современный компилятор будет принимать несколько побитовых операторов в выражении, таком как O_RDONLY | O_WRONLY
, и выполнять побитовое ИЛИ при компиляции исходного кода и заменять выражение значением вычисляемого выражения.
В большинстве компьютерных архитектур побитовые операторы выполняются с использованием регистров, в которые загружаются данные, а затем выполняется побитовая операция. Для 32-битной архитектуры использование 32-битной переменной для хранения набора битов естественным образом вписывается в регистры ЦП, так же как в 64-битной архитектуре использование 32- или 64-битной переменной для хранения набора битов естественным образом вписывается в регистры. Это естественное соответствие позволяет выполнять несколько побитовых операций над одними и теми же переменными без необходимости выполнять выборку из кэша ЦП или основной памяти.
Побитовые операторы C почти всегда имеют аналог машинной инструкции ЦП, так что побитовые операторы C имеют почти точно такую же операцию ЦП, поэтому результирующий машинный код, сгенерированный компилятором, достаточно эффективен.
Компактное представление битовых флагов можно легко увидеть, используя unsigned long
для передачи 32 различных флагов или unsigned long long
для передачи 64 различных флагов в функцию. Массив unsigned char
можно использовать для передачи гораздо большего количества флагов, используя метод смещения массива и битового флага вместе с набором макросов процессора C или набором функций для управления массивом.
Некоторые примеры того, что возможно
Побитовые операторы очень похожи на логические операторы, используемые с множествами, и представление множеств с помощью битовых флагов работает хорошо. Если у вас есть набор, содержащий операнды, некоторые из которых не должны использоваться с некоторыми другими флагами, то использование побитовых операторов и маскирование битов позволяет легко увидеть, указаны ли оба конфликтующих флага.
#define FLAG_1 0x00000001L // a required flag if FLAG_2 is specified
#define FLAG_2 0x00001000L // must not be specified with FLAG_3
#define FLAG_3 0x00002000L // must not be specified with FLAG_2
int func (unsigned long ulFlags)
{
// check if both FLAG_2 and FLAG_3 are specified. if so error
// we do a bitwise And to isolate specific bits and then compare that
// result with the bitwise Or of the bits for equality. this approach
// makes sure that a check for both bits is turned on.
if (ulFlags & (FLAG_2 | FLAG_3) == (FLAG_2 | FLAG_3)) return -1;
// check to see if either FLAG_1 or FLAG_3 is set we can just do a
// bitwise And against the two flags and if either one or both are set
// then the result is non-zero.
if (ulFlags & (FLAG_1 | FLAG_3)) {
// do stuff if either or both FLAG_1 and/or FLAG_3 are set
}
// check that required option FLAG_1 is specified if FLAG_2 is specified.
// we are using zero is boolean false and non-zero is boolean true in
// the following. the ! is the logical Not operator so if FLAG_1 is
// not set in ulFlags then the expression (ulFlags & FLAG_1) evaluates
// to zero, False, and the Not operator inverts the False to True or
// if FLAG_1 is set then (ulFlags & FLAG_1) evaluates to non-zero, True,
// and the Not operator inverts the True to False. Both sides of the
// logical And, &&, must evaluate True in order to trigger the return.
if ((ulFlags & FLAG_2) && ! (ulFlags & FLAG_1)) return -2;
// other stuff
}
Например, см. краткий обзор Использование select() для неблокирующих сокетов. стандартного интерфейса socket()
использования битовых флагов и функции select()
для примеров использования абстракции манипулирования наборами, аналогичной тому, что можно сделать с битовыми флагами. Функции socket
позволяют задавать различные характеристики, такие как неблокировка, за счет использования битовых флагов с функцией fcntl()
, а функция select()
имеет связанный набор функций/макросов (FD_SET()
, FD_ZERO()
и т. д.), который обеспечивает абстракцию для указывая, какие дескрипторы сокета должны отслеживаться в файле select()
. Я не имею в виду, что наборы сокетов select()
являются битовыми картами, хотя я полагаю, что в оригинальной UNIX они были такими. Однако абстрактный дизайн select()
и связанных с ним утилит предоставляет своего рода наборы, которые могут быть реализованы с помощью битовых флагов.
Вычисление переменной, содержащей битовые флаги, также может быть быстрее, проще, эффективнее и читабельнее. Например, в функции, вызываемой с некоторыми из определенных флагов:
#define ITEM_FLG_01 0x0001
#define ITEM_FLG_02 0x0002
#define ITEM_FLG_03 0x0101
#define ITEM_FLG_04 0x0108
#define ITEM_FLG_05 0x0200
#define ITEM_FLG_SPL1 (ITEM_FLG_01 | ITEM_FLG_02)
может быть оператор switch()
, такой как:
switch (bitwiseflags & ITEM_FLG_SPL1) {
case ITEM_FLG_01 | ITEM_FLG_02:
// do things if both ITEM_FLG_01 and ITEM_FLG_02 are both set
break;
case ITEM_FLG_01:
// do things if ITEM_FLG_01 is set
break;
case ITEM_FLG_02:
// do things if ITEM_FLG_02 is set
break;
default:
// none of the flags we are looking for are set so error
return -1;
}
И вы можете сделать несколько коротких и простых выражений, таких как следующие, используя те же определения, что и выше.
// test if bitwiseflags has bit ITEM_FLG_5 set and if so then call function
// doFunc().
(bitwiseflags & ITEM_FLG_5) == ITEM_FLG_5 && doFunc();
Дополнение: метод для очень больших наборов битовых флагов
См. этот ответ, Создание переменных битовых флагов с большим количеством флагов или как создавать большие числа битовой ширины для подхода для больших наборы битовых флагов больше, чем может поместиться в 32-битной или 64-битной переменной.
person
Richard Chambers
schedule
18.03.2018
||
явно неверны - person harold   schedule 19.03.2018|
и||
разные вещи. Первый — это побитовое ИЛИ, а второй — логический оператор ИЛИ. - person Pablo   schedule 19.03.2018B
в данном случае не выполняется; флаги вfopen("foo", "r")
занимают меньше места (2 байта), чем флаги вopen("foo", O_RDONLY)
(скорее всего, целое число с 4 байтами). - person ensc   schedule 19.03.2018