Digamos que tengo un conjunto de banderas, codificadas en uint16_t flags
. Por ejemplo, AMAZING_FLAG = 0x02
. Ahora tengo una función. Esta función necesita verificar si quiero cambiar la bandera, porque si quiero hacer eso, necesito escribir en flash. Y eso es caro. Por lo tanto, quiero un cheque que me diga si flags & AMAZING_FLAG
es igual a doSet
. Esta es la primera idea:
setAmazingFlag(bool doSet)
{
if ((flags & AMAZING_FLAG) != (doSet ? AMAZING_FLAG : 0)) {
// Really expensive thing
// Update flags
}
}
Esta no es una declaración if intuitiva. Siento que debería haber una mejor manera, algo como:
if ((flags & AMAZING_FLAG) != doSet){
}
Pero esto en realidad no funciona, true
parece ser igual a 0x01
.
Entonces, ¿hay una manera ordenada de comparar un poco con un booleano?
c
gcc
boolean
bitwise-operators
Cheiron
fuente
fuente
(flags & AMAZING_FLAG) && doSet
:?Respuestas:
Para convertir cualquier número que no sea cero a 1 (verdadero), hay un viejo truco: aplique el
!
operador (no) dos veces.fuente
(bool)(flags & AMAZING_FLAG) != doSet
, lo que me parece más directo. (Aunque esto sugiere que el Sr. Microsoft tiene un problema con esto). Además,((flags & AMAZING_FLAG) != 0)
es probablemente exactamente lo que compila y es absolutamente explícito.((bool)(flags & AMAZING_FLAG) != doSet)
tiene el mismo efecto(((flags & AMAZING_FLAG) != 0) != doSet)
y probablemente ambos compilarán exactamente lo mismo. Lo sugerido(!!(flags & AMAZING_FLAG) != doSet)
es equivalente, e imagino que también compilará lo mismo. Es una cuestión de gustos cuál crees que es más claro: el(bool)
reparto requiere que recuerdes cómo funcionan los conversiones y conversiones a _Bool; la!!
requiera otro tipo de gimnasia mental, o que sepa el "truco".Necesita convertir la máscara de bits en una declaración booleana, que en C es equivalente a valores
0
o1
.(flags & AMAZING_FLAG) != 0
. La forma mas comun.!!(flags & AMAZING_FLAG)
. Algo común, también está bien usar, pero un poco críptico.(bool)(flags & AMAZING_FLAG)
. Moderno estilo C desde C99 y más allá solamente.Tome cualquiera de las alternativas anteriores, luego compárela con su valor booleano usando
!=
o==
.fuente
Desde un punto de vista lógico,
flags & AMAZING_FLAG
es solo una operación de bits que enmascara todos los demás indicadores. El resultado es un valor numérico.Para recibir un valor booleano, usaría una comparación
y ahora puede comparar este valor lógico con
doSet
.En C puede haber abreviaturas, debido a las reglas de conversión implícitas de números a valores booleanos. Entonces también podrías escribir
para escribir eso más conciso. Pero la versión anterior es mejor en términos de legibilidad.
fuente
Puede crear una máscara basada en el
doSet
valor:Ahora su cheque puede verse así:
En algunas arquitecturas,
!!
puede compilarse en una rama y, de este modo, puede tener dos ramas:!!(expr)
doSet
La ventaja de mi propuesta es una rama única garantizada.
Nota: asegúrese de no introducir un comportamiento indefinido desplazándose a la izquierda más de 30 (suponiendo que el entero sea de 32 bits). Esto se puede lograr fácilmente mediante un
static_assert(AMAZING_FLAG_IDX < sizeof(int)*CHAR_BIT-1, "Invalid AMAZING_FLAG_IDX");
fuente
AMAZING_FLAG
en términos deAMAZING_FLAG_IDX
(p#define AMAZING_FLAG ((uint16_t)(1 << AMAZING_FLAG_IDX))
. Ej. ), Para que no tenga los mismos datos definidos en dos lugares, de modo que uno pueda actualizarse (digamos, de0x4
a0x8
) mientras que el otro (IDX
de2
) no se modifica .Hay varias formas de realizar esta prueba:
El operador ternario podría generar saltos costosos:
También puede usar la conversión booleana, que puede o no ser eficiente:
O su alternativa equivalente:
Si la multiplicación es barata, puede evitar saltos con:
Si
flags
no está firmado y el compilador es muy inteligente, la siguiente división podría compilarse con un simple cambio:Si la arquitectura usa la aritmética del complemento a dos, aquí hay otra alternativa:
Alternativamente, uno podría definir
flags
como una estructura con campos de bits y usar una sintaxis mucho más simple y legible:Por desgracia, este enfoque generalmente está mal visto porque la especificación de los campos de bits no permite un control preciso sobre la implementación a nivel de bits.
fuente