Побитовое ИЛИ против добавления флагов

16

Я видел, как другие использовали Bitwise-OR для объединения флагов раньше:

#define RUN 0x01
#define JUMP 0x02
#define SHOOT 0x04

const byte madPerson = RUN | JUMP | SHOOT;

Я тоже так делаю.

Но я также видел некоторые (не так много) флаги объединения, используя сложение:

#define RUN 0x01
#define JUMP 0x02
#define SHOOT 0x04

const byte madPerson = RUN + JUMP + SHOOT;

Какой из них более «читабелен»? (Как вы думаете, какой из них узнает больше людей?) Каков «стандартный» способ сделать это? Какой из них вы предпочитаете?

Матин Улхак
источник
Это ТАК вопрос. Рассмотрим , используя нечто вроде 1<<0, 1<<1, 1<<2и так далее. Когда у вас много флагов, он становится более читабельным, более легким в обслуживании, менее подверженным ошибкам. Например, если вы упаковываете все 64 бита 64-битного целого, вы действительно хотите избежать опечаток :) То, как вы представляете 1, также важно. Для 64-разрядного целого числа в VS2010 я думаю, что-то 1UI64, или что-то в этом роде. Использование неправильного типа может укусить вас.
Работа
3
@Job: не вопрос StackOverflow, потому что он спрашивает о читабельности, узнаваемости, предпочтениях и лучших практиках. Нет единого объективного ответа на это; это принадлежит здесь.
Макнейл

Ответы:

34

Побитовое ИЛИ.

Дополнение опасно.

Рассмотрим пример, где бандит - это человек, а злой бандит - это бандит, который говорит и стреляет. Позже вы решаете, что все бандиты должны стрелять, но вы забыли определение злого бандита и не снимаете флаг стрельбы.

#define PERSON 1 << 0
#define SPEAKS 1 << 1
#define SHOOTS 1 << 2
#define INVINCIBLE 1 << 3
const byte bandit = PERSON | SHOOTS;                    // 00000101
const byte angryBandit_add = bandit + SPEAKS + SHOOTS;  // 00001011 error
const byte angryBandit_or = bandit | SPEAKS | SHOOTS;   // 00000111 ok

Если бы вы использовали angryBandit_addсвою игру, то теперь у вас была бы сбивающая с толку логическая ошибка из-за наличия злых бандитов, которые не могут стрелять или быть убиты.

Если бы вы использовали angryBandit_orхудшее, что у вас было бы, это избыточно | SHOOTS.

По тем же причинам побитовое НЕ безопаснее, чем вычитание для удаления флагов.

doppelgreener
источник
11

побитовое ИЛИ передает намерение более четко

также побитовое ИЛИ должно быть более эффективным

Стивен А. Лоу
источник
+1 действительно, я также думаю, что OR делает более ясным, что это флаги, но что касается эффективности, то есть языки, в которых побитовые операции выполняются медленно, например, JavaScript, все числа которых равны 64 числам с плавающей запятой, операторы должны выполнять неявное преобразование для них.
Иво Ветцель
1
Учитывая пример OP, я не думаю, что одна строка OR или сложения отрицательно повлияет на скорость выполнения программы.
Железный человек
1
@Greg: тем более что расчет в этом примере будет сделан во время компиляции. :-)
Carson63000
В дополнение к выражению намерений, довольно часто можно увидеть это на многих языках, включая, помимо прочего, ADA, C #, Java ...
Кен Хендерсон,
2
«должен» это очень большое слово в этом бизнесе. Хотя вряд ли вы столкнетесь с этой проблемой сегодня, у меня очень ясные воспоминания о работе над процессором, у которого не было инструкции побитового ИЛИ. Вы можете поразрядно-И в одной инструкции, и вы можете поразрядно-XOR в одной инструкции, но побитово-ИЛИ потребовалось две: немедленное побитовое И для выключения бита и немедленное побитовое XOR для дополнения недавно очищенного бита , что, конечно, установить его.
Джон Р. Штром