Скажем, у меня есть набор флагов, закодированных в uint16_t flags
. Например, AMAZING_FLAG = 0x02
. Теперь у меня есть функция. Эта функция должна проверить, хочу ли я изменить флаг, потому что, если я хочу это сделать, мне нужно записать на флэш-память. И это дорого. Поэтому я хочу чек, который говорит мне, flags & AMAZING_FLAG
равен ли doSet
. Это первая идея:
setAmazingFlag(bool doSet)
{
if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
// Really expensive thing
// Update flags
}
}
Это не интуитивное утверждение if. Я чувствую, что должен быть лучший способ, что-то вроде:
if ((flags & AMAZING_FLAG) != doSet){
}
Но это на самом деле не работает, true
похоже, равно 0x01
.
Итак, есть ли хороший способ сравнить немного с логическим значением?
c
gcc
boolean
bitwise-operators
Cheiron
источник
источник
(flags & AMAZING_FLAG) && doSet
?Ответы:
Чтобы преобразовать любое ненулевое число в 1 (true), существует старый прием:
!
дважды примените оператор (not).источник
(bool)(flags & AMAZING_FLAG) != doSet
- что я считаю более прямым. (Хотя это говорит о том, что у мистера Майкрософт есть проблема с этим.) Кроме того,((flags & AMAZING_FLAG) != 0)
это, вероятно, именно то, к чему он компилируется, и является абсолютно явным.((bool)(flags & AMAZING_FLAG) != doSet)
имеет тот же эффект, что(((flags & AMAZING_FLAG) != 0) != doSet)
и оба они, вероятно, будут компилировать в одно и то же. Предложенное(!!(flags & AMAZING_FLAG) != doSet)
эквивалентно, и я думаю, что скомпилируется с тем же. Это вопрос вкуса, который, на ваш взгляд, более понятен:(bool)
актеры требуют, чтобы вы помнили, как работают преобразования и преобразования в _Bool; Это!!
требует некоторой другой умственной гимнастики, или для вас, чтобы узнать "хитрость".Вам необходимо преобразовать битовую маску в логическое выражение, которое в C эквивалентно значениям
0
или1
.(flags & AMAZING_FLAG) != 0
, Самый распространенный способ.!!(flags & AMAZING_FLAG)
, Несколько распространенный, также хорошо, чтобы использовать, но немного загадочный.(bool)(flags & AMAZING_FLAG)
, Современный C-путь только от C99.Возьмите любой из приведенных выше вариантов, затем сравните его с вашим логическим значением, используя
!=
или==
.источник
С логической точки зрения,
flags & AMAZING_FLAG
это всего лишь битовая операция, маскирующая все остальные флаги. Результатом является числовое значение.Чтобы получить логическое значение, вы должны использовать сравнение
и теперь можно сравнить это логическое значение с
doSet
.В Си могут быть сокращения из-за неявных правил преобразования чисел в логические значения. Таким образом, вы могли бы также написать
написать это более кратко. Но предыдущая версия лучше с точки зрения читабельности.
источник
Вы можете создать маску на основе
doSet
значения:Теперь ваш чек может выглядеть так:
На некоторых архитектурах
!!
может быть скомпилирован в ветвь, и тем самым у вас может быть две ветки:!!(expr)
doSet
Преимуществом моего предложения является гарантированная единая ветка.
Примечание: убедитесь, что вы не вводите неопределенное поведение, сдвигая влево более чем на 30 (при условии, что целое число составляет 32 бита). Это может быть легко достигнуто
static_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");
источник
AMAZING_FLAG
в терминахAMAZING_FLAG_IDX
(например#define AMAZING_FLAG ((uint16_t)(1 << AMAZING_FLAG_IDX))
), чтобы у вас не было одинаковых данных, определенных в двух местах, так что одно можно было бы обновить (скажем, от0x4
до0x8
), а другое (IDX
из2
) оставить без изменений. ,Есть несколько способов выполнить этот тест:
Тернарный оператор может генерировать дорогостоящие переходы:
Вы также можете использовать логическое преобразование, которое может быть или не быть эффективным:
Или его эквивалентная альтернатива:
Если умножение дешево, вы можете избежать скачков с:
Если он
flags
не подписан, а компилятор очень умен, приведенное ниже разделение может скомпилировать простой сдвиг:Если в архитектуре используется арифметика с дополнением до двух, здесь есть еще одна альтернатива:
Альтернативно, можно определить
flags
как структуру с битовыми полями и использовать намного более простой читаемый синтаксис:Увы, этот подход обычно не одобряется, потому что спецификация битовых полей не позволяет точно контролировать реализацию битового уровня.
источник