Bitowe-LUB vs Dodawanie flag

16

Widziałem wcześniej, jak inni używają Bitwise-OR do łączenia flag:

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

const byte madPerson = RUN | JUMP | SHOOT;

Tak też to robię.

Ale widziałem także niektóre (nie tak wiele) łączące flagi przy użyciu dodawania:

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

const byte madPerson = RUN + JUMP + SHOOT;

Który jest bardziej „czytelny”? (Jak myślisz, który z nich rozpozna więcej osób?) Jaki jest „standardowy” sposób na zrobienie tego? Który wolisz?

Mateen Ulhaq
źródło
To jest pytanie SO. Rozważyć użycie coś jak 1<<0, 1<<1, 1<<2i tak dalej. Gdy masz wiele flag, staje się bardziej czytelny, łatwiejszy w utrzymaniu, mniej podatny na błędy. Na przykład, jeśli pakujesz wszystkie 64 bity 64-bitowej int, naprawdę chcesz uniknąć literówek :) 1Ważne jest również to, jak reprezentujesz . Wydaje mi się, że dla 64-bitowej liczby całkowitej w VS2010 jest 1UI64coś takiego. Użycie niewłaściwego typu może cię ugryźć.
Job
3
@Job: To nie jest pytanie StackOverflow, ponieważ pyta o czytelność, rozpoznawalność, preferencje i najlepsze praktyki. Nie ma na to jednoznacznej odpowiedzi; należy tutaj.
Macneil,

Odpowiedzi:

34

Bitowe-LUB.

Dodawanie jest niebezpieczne.

Rozważ przykład, w którym bandytą jest osoba, a zły bandyta to bandyta, który mówi i strzela. Później decydujesz, że wszyscy bandyci powinni strzelać, ale zapomniałeś o definicji gniewnego bandyty i nie usuwaj flagi strzelania.

#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

Gdybyś użył angryBandit_addswojej gry, miałby teraz kłopotliwy błąd logiczny posiadania wściekłych bandytów, którzy nie mogą strzelać ani zostać zabici.

Jeśli użyłeś angryBandit_ornajgorszego, co miałbyś, to zbędne | SHOOTS.

Z podobnych powodów bitowe NIE jest bezpieczniejsze niż odejmowanie do usuwania flag.

doppelgreener
źródło
11

bitowe-LUB przekazuje intencję wyraźniej

również bitowe-LUB powinno być bardziej wydajne

Steven A. Lowe
źródło
+1 rzeczywiście, myślę również, że OR wyjaśnia, że ​​są to flagi, ale jeśli chodzi o wydajność, istnieją języki, w których operacje bitowe są powolne, np. JavaScript, wszystkie liczby to 64 zmiennoprzecinkowe, operatory bitowe muszą dokonać na nich niejawnej konwersji.
Ivo Wetzel,
1
Biorąc pod uwagę przykład PO, nie sądzę, aby jedna linia OR lub dodanie wpłynęło negatywnie na szybkość wykonywania programu.
Tin Man,
1
@Greg: zwłaszcza, że ​​obliczenia w tym przykładzie zostaną wykonane w czasie kompilacji. :-)
Carson63000
Oprócz przekazywania intencji dość często można to zobaczyć w wielu językach, w tym między innymi w ADA, C #, Java ...
Ken Henderson
2
„powinien” to bardzo duże słowo w tym biznesie. Chociaż jest bardzo mało prawdopodobne, że napotkasz dzisiaj ten problem, mam bardzo wyraźne wspomnienia z pracy na procesorze, który nie miał instrukcji bitowej LUB. Możesz bitowo-ORAZ w jednym instruktażu, i możesz bitowo-XOR w jednej instrukcji, ale bitowo-LUB wziął dwa: natychmiastowo bitowo-ORAZ, aby wyłączyć bit, i natychmiastowo-bitowo-XOR, aby uzupełnić nowo wyczyszczony bit , co oczywiście to ustawiło.
John R. Strohm,