Powiedzmy, że mam zestaw flag zakodowanych w uint16_t flags
. Na przykład AMAZING_FLAG = 0x02
. Teraz mam funkcję. Ta funkcja musi sprawdzić, czy chcę zmienić flagę, ponieważ jeśli chcę to zrobić, muszę napisać, aby flashować. A to jest drogie. Dlatego chcę testu, który mówi mi, czy flags & AMAZING_FLAG
jest równy doSet
. To jest pierwszy pomysł:
setAmazingFlag(bool doSet)
{
if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
// Really expensive thing
// Update flags
}
}
Nie jest to intuicyjna instrukcja if. Wydaje mi się, że powinien istnieć lepszy sposób, na przykład:
if ((flags & AMAZING_FLAG) != doSet){
}
Ale to tak naprawdę nie działa, true
wydaje się być równe 0x01
.
Czy istnieje dobry sposób na porównanie z wartością logiczną?
c
gcc
boolean
bitwise-operators
Cheiron
źródło
źródło
(flags & AMAZING_FLAG) && doSet
:?Odpowiedzi:
Aby przekonwertować dowolną niezerową liczbę na 1 (prawda), istnieje stara sztuczka: zastosuj
!
(nie) operator dwa razy.źródło
(bool)(flags & AMAZING_FLAG) != doSet
- co uważam za bardziej bezpośrednie. (Chociaż sugeruje to , że pan Microsoft ma z tym problem.) Również((flags & AMAZING_FLAG) != 0)
prawdopodobnie jest dokładnie tym, do czego się kompiluje i jest absolutnie wyraźny.((bool)(flags & AMAZING_FLAG) != doSet)
ma taki sam efekt jak(((flags & AMAZING_FLAG) != 0) != doSet)
i oboje prawdopodobnie skompilują się do dokładnie tego samego. Sugerowane(!!(flags & AMAZING_FLAG) != doSet)
jest równoważne i wyobrażam sobie, że skompiluje się również do tego samego. To kwestia gustu, która według Ciebie jest wyraźniejsza:(bool)
obsada wymaga zapamiętania, jak działają rzutowania i konwersje do _Bool;!!
wymaga pewnych innych gimnastykę umysłu, albo, aby poznać „trick”.Musisz przekonwertować maskę bitową na instrukcję boolean, która w C jest równoważna wartościom
0
lub1
.(flags & AMAZING_FLAG) != 0
. Najczęstszy sposób.!!(flags & AMAZING_FLAG)
. Nieco powszechne, również OK, aby użyć, ale nieco tajemnicze.(bool)(flags & AMAZING_FLAG)
. Nowoczesny sposób C tylko z C99 i nie tylko.Wybierz dowolną z powyższych opcji, a następnie porównaj ją z wartością logiczną za pomocą
!=
lub==
.źródło
Z logicznego punktu widzenia
flags & AMAZING_FLAG
jest to tylko niewielka operacja maskująca wszystkie inne flagi. Wynik jest wartością liczbową.Aby otrzymać wartość logiczną, użyłbyś porównania
i może teraz porównać tę wartość logiczną z
doSet
.W C mogą występować skróty z powodu niejawnych reguł konwersji liczb na wartości boolowskie. Więc możesz też pisać
napisać to bardziej zwięźle. Ale poprzednia wersja jest lepsza pod względem czytelności.
źródło
Możesz utworzyć maskę na podstawie
doSet
wartości:Teraz czek może wyglądać następująco:
Na niektórych architekturach
!!
może być skompilowany do gałęzi, dzięki czemu możesz mieć dwie gałęzie:!!(expr)
doSet
Zaletą mojej propozycji jest zagwarantowanie jednego oddziału.
Uwaga: upewnij się, że nie wprowadzasz niezdefiniowanego zachowania, przesuwając w lewo o więcej niż 30 (zakładając, że liczba całkowita to 32 bity). Można to łatwo osiągnąć za pomocą
static_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");
źródło
AMAZING_FLAG
w kategoriachAMAZING_FLAG_IDX
(np.#define AMAZING_FLAG ((uint16_t)(1 << AMAZING_FLAG_IDX))
), Aby nie zdefiniować tych samych danych w dwóch miejscach, tak aby jedno z nich mogło zostać zaktualizowane (powiedzmy od0x4
do0x8
), podczas gdy drugie (IDX
z2
) pozostanie niezmienione .Istnieje wiele sposobów wykonania tego testu:
Operator trójskładnikowy może generować kosztowne skoki:
Możesz także użyć konwersji logicznej, która może, ale nie musi być skuteczna:
Lub jego odpowiednik:
Jeśli mnożenie jest tanie, możesz uniknąć skoków za pomocą:
Jeśli nie
flags
jest podpisany, a kompilator jest bardzo inteligentny, poniższy podział może się skompilować do prostej zmiany:Jeśli architektura używa arytmetyki dopełniania dwóch, oto kolejna alternatywa:
Alternatywnie można zdefiniować
flags
jako strukturę z polami bitowymi i zastosować znacznie prostszą czytelną składnię:Niestety, takie podejście jest zwykle lekceważone, ponieważ specyfikacja pól bitowych nie pozwala na precyzyjną kontrolę nad implementacją na poziomie bitów.
źródło