Angenommen, ich habe eine Reihe von Flags, die in einem uint16_t codiert sind flags
. Zum Beispiel AMAZING_FLAG = 0x02
. Jetzt habe ich eine Funktion. Diese Funktion muss prüfen, ob ich das Flag ändern möchte, denn wenn ich das tun möchte, muss ich in Flash schreiben. Und das ist teuer. Daher möchte ich eine Überprüfung, die mir sagt, ob flags & AMAZING_FLAG
gleich ist doSet
. Dies ist die erste Idee:
setAmazingFlag(bool doSet)
{
if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
// Really expensive thing
// Update flags
}
}
Dies ist keine intuitive if-Anweisung. Ich denke, es sollte einen besseren Weg geben, so etwas wie:
if ((flags & AMAZING_FLAG) != doSet){
}
Aber das funktioniert eigentlich nicht, true
scheint gleich zu sein 0x01
.
Gibt es eine gute Möglichkeit, ein bisschen mit einem Booleschen Wert zu vergleichen?
c
gcc
boolean
bitwise-operators
Cheiron
quelle
quelle
(flags & AMAZING_FLAG) && doSet
?Antworten:
Um eine Zahl ungleich Null in 1 (wahr) umzuwandeln, gibt es einen alten Trick: Wenden Sie den
!
Operator (nicht) zweimal an.quelle
(bool)(flags & AMAZING_FLAG) != doSet
- was ich direkter finde. ( Dies deutet jedoch darauf hin, dass Herr Microsoft ein Problem damit hat.) Auch((flags & AMAZING_FLAG) != 0)
ist es wahrscheinlich genau das, worauf es kompiliert und ist absolut explizit.((bool)(flags & AMAZING_FLAG) != doSet)
hat den gleichen Effekt wie(((flags & AMAZING_FLAG) != 0) != doSet)
und beide werden wahrscheinlich genau das gleiche kompilieren. Der Vorschlag(!!(flags & AMAZING_FLAG) != doSet)
ist äquivalent, und ich kann mir vorstellen, dass er auch zu derselben Sache kompiliert wird. Es ist ganz und gar eine Geschmackssache, die Sie für klarer halten: Bei der(bool)
Besetzung müssen Sie sich daran erinnern, wie Besetzungen und Konvertierungen in _Bool funktionieren. Das!!
erfordert eine andere mentale Gymnastik oder damit Sie den "Trick" kennen.Sie müssen die Bitmaske in eine boolesche Anweisung konvertieren, die in C den Werten
0
oder entspricht1
.(flags & AMAZING_FLAG) != 0
. Der häufigste Weg.!!(flags & AMAZING_FLAG)
. Etwas üblich, auch in Ordnung, aber etwas kryptisch.(bool)(flags & AMAZING_FLAG)
. Moderner C-Weg ab C99 und darüber hinaus.Nehmen Sie eine der oben genannten Alternativen und vergleichen Sie sie mit Ihrem Booleschen Wert unter Verwendung von
!=
oder==
.quelle
Aus logischer Sicht
flags & AMAZING_FLAG
ist nur eine Bitoperation, die alle anderen Flags maskiert. Das Ergebnis ist ein numerischer Wert.Um einen booleschen Wert zu erhalten, würden Sie einen Vergleich verwenden
und kann nun diesen logischen Wert mit vergleichen
doSet
.In C kann es aufgrund der impliziten Konvertierungsregeln von Zahlen in boolesche Werte Abkürzungen geben. Du könntest also auch schreiben
um das knapper zu schreiben. Die frühere Version ist jedoch in Bezug auf die Lesbarkeit besser.
quelle
Sie können eine Maske basierend auf dem
doSet
Wert erstellen :Jetzt kann Ihr Scheck folgendermaßen aussehen:
Auf einigen Architekturen
!!
kann zu einem Zweig kompiliert werden, und auf diese Weise können Sie zwei Zweige haben:!!(expr)
doSet
Der Vorteil meines Vorschlags ist eine garantierte Einzelniederlassung.
Hinweis: Stellen Sie sicher, dass Sie kein undefiniertes Verhalten einführen, indem Sie um mehr als 30 nach links verschieben (vorausgesetzt, die Ganzzahl beträgt 32 Bit). Dies kann leicht erreicht werden durch a
static_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");
quelle
AMAZING_FLAG
in Bezug aufAMAZING_FLAG_IDX
(z. B.#define AMAZING_FLAG ((uint16_t)(1 << AMAZING_FLAG_IDX))
), damit Sie nicht dieselben Daten an zwei Stellen definiert haben, sodass eine aktualisiert werden kann (z. B. von0x4
bis0x8
), während die andere (IDX
von2
) unverändert bleibt .Es gibt mehrere Möglichkeiten, diesen Test durchzuführen:
Der ternäre Operator kann kostspielige Sprünge erzeugen:
Sie können auch die boolesche Konvertierung verwenden, die möglicherweise effizient ist oder nicht:
Oder eine gleichwertige Alternative:
Wenn die Multiplikation billig ist, können Sie Sprünge vermeiden mit:
Wenn
flags
es nicht signiert ist und der Compiler sehr intelligent ist, kann die folgende Unterteilung zu einer einfachen Verschiebung kompiliert werden:Wenn die Architektur die Zweierkomplementarithmetik verwendet, gibt es hier eine andere Alternative:
Alternativ könnte man
flags
eine Struktur mit Bitfeldern definieren und eine viel einfachere und lesbare Syntax verwenden:Leider wird dieser Ansatz normalerweise verpönt, da die Spezifikation von Bitfeldern keine genaue Kontrolle über die Implementierung auf Bitebene ermöglicht.
quelle