Usar if (!!(expr)) en lugar de if (expr)

10 minutos de lectura

avatar de usuario
Yaseen

Al leer el código de ejemplo proporcionado por Texas Instruments para su Etiqueta del sensor Me encontré con el siguiente fragmento.

void SensorTagIO_processCharChangeEvt(uint8_t paramID) { 
    ...

    if (!!(ioValue & IO_DATA_LED1)) {
        PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_ON);
    } else {
        PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_OFF);
    }

    if (!!(ioValue & IO_DATA_LED2)) {
        PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_ON);
    } else {
        PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_OFF);
    }

    if (!!((ioValue & IO_DATA_BUZZER))) {
        Clock_start(buzzClockHandle);
    }
    ...
}

La declaración es así (en el mismo archivo).

#define IO_DATA_LED1   0x01
static uint8_t ioValue;

Lo hace if (!!(ioValue & IO_DATA_LED1)) ofrecer alguna ventaja sobre if (ioValue & IO_DATA_LED1)?

  • stackoverflow.com/q/2527086/3049655

    – Spikatrix

    20 de febrero de 2016 a las 12:49

  • @CoolGuy: Eso no es un engaño. Hay una razón por la cual el argumento en la pregunta vinculada se convierte en un valor booleano. Aquí es inútil, ya que el valor en sí no se procesa.

    – demasiado honesto para este sitio

    20 de febrero de 2016 a las 13:12


  • @Olaf No, __builtin_expect(x, 0) y __builtin_expect(!!(x),0) debe comportarse por igual, por lo tanto, es inútil allí, también

    – CTX

    20 de febrero de 2016 a las 16:54

  • Por supuesto, ninguna de las respuestas presentadas aquí hasta ahora proporciona una idea de por qué if (!!((ioValue & IO_DATA_BUZZER))) { contendría doble parens alrededor del bit a bit-y. Cosas como esa me hacen preguntarme si no estamos viendo código que sufrió daños de cortar y pegar/buscar y reemplazar.

    – FeRD

    20 de febrero de 2016 a las 18:54

Aplicando el no lógico (!) El operador dos veces tiene el propósito de normalizar un valor para que sea 0 o 1. En una expresión de control de una declaración if, eso no hace ninguna diferencia. A la sentencia if sólo le importa que el valor sea cero o distinto de cero, el pequeño !! el baile es completamente inútil.

Algunas guías de estilo de codificación pueden exigir este tipo de baile, lo que podría ser la razón por la cual el código TI que publicaste lo hace. Aunque no he visto ninguno que lo haga.

  • Acordado. También es muy poco común en la programación integrada. Sospecho que es alguna reliquia de desenrollar, por ejemplo, un BITTEST macro que se supone que devuelve el valor de un bit, no solo la información establecida/borrada para su posterior procesamiento. Este código no suele ser escrito por estudiantes, etc., que no tienen mucha experiencia (y no están muy bien pagados), por lo que tienden a usar patrones existentes y no optimizan/mejoran bien.

    – demasiado honesto para este sitio

    20 de febrero de 2016 a las 13:20

  • @GOTO0: ¿Cuál puede que hazlos desarrolladores de Java experimentadospero no desarrolladores C experimentados.

    – demasiado honesto para este sitio

    20 de febrero de 2016 a las 13:45

  • @Olaf Como Java es un lenguaje fuertemente tipado, no hay otro tipo de operando que los booleanos a los que se pueda aplicar la negación lógica, lo que hace inútil la doble negación. La única razón posible en la que puedo pensar podría ser forzar un NPE cuando se aplica a booleanos en caja.

    – John Dvorak

    20 de febrero de 2016 a las 13:55

  • Algunos compiladores advierten sobre construcciones como if (a = b), ya que es probable que estos sean erróneos. Podrías evitar la advertencia con if ((a = b) != 0)pero eso es feo como el pecado. if (!!(a = b)) es un poco más conciso

    – FEO

    20 de febrero de 2016 a las 16:05

  • @EOF Por lo general, los compiladores recogen if ((a = b)). Creo que esta convención se remonta a pelusa.

    – fuz

    20 de febrero de 2016 a las 19:05

avatar de usuario
Damián Yerrick

La expresion !!xo !(!x)significa 1 si x es un valor verdadero (un número distinto de cero o un puntero no nulo), de lo contrario 0. Es equivalente a x != 0y es casi lo mismo que C99 (_Bool)x pero disponible en compiladores anteriores a C99 o cuyos desarrolladores han optado por no implementar C99 (como cc65 que apunta al MOS 6502).

El condicional en su conjunto es equivalente a lo siguiente:

if (ioValue & IO_DATA_LED1) {
    /* what to do if the IO_DATA_LED1 bit is true */
} else {
    /* what to do if the IO_DATA_LED1 bit is false */
}

En C, significa “si el AND bit a bit de esos dos valores es distinto de cero, ejecute el bloque”.

Pero algunas guías de estilo de codificación pueden prohibir un AND bit a bit (&) en el nivel superior de un if condición de la declaración, asumiendo que es un error tipográfico para el AND lógico (&&). Está en la misma clase de errores que usar = (asignación) en lugar de == (comparación de igualdad), para el cual muchos compiladores ofrecen un diagnóstico. Opciones de advertencia de GCC describe diagnósticos como estos:

-Wlogical-op: Advertir sobre usos sospechosos de operadores lógicos en expresiones. Esto incluye el uso de operadores lógicos en contextos en los que es probable que se espere un operador bit a bit.

-Wparentheses: advierte si se omiten los paréntesis en ciertos contextos, como cuando hay una tarea en un contexto donde se espera un valor de verdad

El uso de una paráfrasis como (a & B) != 0, (_Bool)(a & B)o !!(a & B) comunica al compilador ya otros desarrolladores que el uso de un operador bit a bit fue intencional.

Ver también una respuesta relacionada sobre !!x en JavaScript.

  • Acordado. Inmediatamente miré el código de OP y vi el enmascaramiento de bits: el !! realmente lo deja claro.

    – J…

    20 de febrero de 2016 a las 20:57

  • ¡Gracias! por la explicación.

    – Yaseen

    21 de febrero de 2016 a las 4:45

  • Esta es una mejor respuesta que la respuesta aceptada, que no aborda el aspecto del operador bit a bit.

    – Dan Henderson

    21 de febrero de 2016 a las 18:48

  • Nota sobre “Eso (!!x) es equivalente a x != 0o al C99 (_Bool)x” –> difiere en tipo y tal vez en tamaño.

    – chux – Reincorporar a Monica

    21 de febrero de 2016 a las 22:55


  • Esta parece ser una mejor respuesta que la aceptada.

    – ruso

    23 de febrero de 2016 a las 22:34

En MSVC convertir un número entero a un bool implícitamente en un if declaración puede generar una advertencia. hacerlo a través de !! no es. Puede existir una advertencia similar en otros compiladores.

Entonces, supongamos que el código se compiló con esa advertencia habilitada y la decisión de tratar todas las advertencias como errores, usando !! es una forma corta y portátil de decir “sí, quiero que este número entero sea un bool“.

  • Pero… la expresión de control de un if declaración no se convierte implícitamente. ¿Quizás estás confundiendo C con C++?

    – fuz

    21 de febrero de 2016 a las 15:10


avatar de usuario
tecnosaurio

Aunque silenciar la advertencia del compilador para el bit-wise & es lo más probable, parece que también podría ser el resultado de una refactorización para agregar enumeraciones para facilitar la lectura de:

PIN_setOutputValue(int,int,bool); //function definition
PIN_setOutputValue(hGpioPin, Board_LED1,!!(ioValue & IO_DATA_LED1));
PIN_setOutputValue(hGpioPin, Board_LED2,!!(ioValue & IO_DATA_LED2));
//note: the !! is necessary here in case sizeof ioValue > sizeof bool
//otherwise it may only catch the 1st 8 LED statuses as @M.M points out

a:

enum led_enum {
  Board_LED_OFF = false,
  Board_LED_ON = true
};
PIN_setOutputValue(int,int,bool); //function definition
//...
PIN_setOutputValue(hGpioPin, Board_LED1,!!(ioValue & IO_DATA_LED1)?Board_LED_ON:Board_LED_OFF);
PIN_setOutputValue(hGpioPin, Board_LED2,!!(ioValue & IO_DATA_LED2)?Board_LED_ON:Board_LED_OFF);

Dado que superó el límite de 80 caracteres, se refactorizó para

if (!!(ioValue & IO_DATA_LED1)) {
    PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_ON);
} else {
    PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_OFF);
}

if (!!(ioValue & IO_DATA_LED2)) {
    PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_ON);
} else {
    PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_OFF);
}

Personalmente, hubiera preferido la versión inicial por legibilidad, pero esta versión es común cuando las líneas de código se usan como métrica (me sorprende que no declarara variables para cada estado, estableciera cada estado por separado y luego lo usara).

La próxima versión de este código de “mejores prácticas” podría verse así:

bool boardled1State;
bool boardled2State;
//...

boardled1State = !!(ioValue & IO_DATA_LED1);
boardled2State = !!(ioValue & IO_DATA_LED2);
//...

if (boardled1State) {
    PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_ON);
} else {
    PIN_setOutputValue(hGpioPin, Board_LED1, Board_LED_OFF);
}

if (boardled2State) {
    PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_ON);
} else {
    PIN_setOutputValue(hGpioPin, Board_LED2, Board_LED_OFF);
}
//... and so on

Todo eso se podría haber hecho así:

for (int i=0;i<numleds;i++)
        PIN_setOutputValue(hGpioPin, i ,!!(ioValue & (1<<i)));

avatar de usuario
chux – Reincorporar a Monica

OP está mirando un idioma de codificación antiguo, que tenía algún sentido BITD (en el pasado).

  1. Un uso primario de !! era manejar implementaciones de C que convertían la expresión en un if(expr) a int en lugar de probar contra cero.

Considere lo que sucede cuando expr se convierte en int y luego probado contra 0. (Desde C89, esto no es conforme ya que la prueba debe ser una prueba directa contra 0)

int i;
long li;
double d;

// no problems
if (i & 5) ...
if (d > 4.0) ...

// problems
if (li & 0x10000) ...  (Hint: int is 16-bit)
if (d)                 (d might have a value outside `int` range.

// fix
if (!!(li & 0x10000))
if (!!d)

Entonces, en los cumplidores anteriores a C89 y C89 no conformes y posteriores, usando !! hizo frente a esa debilidad. Algunos viejos hábitos tardan mucho en morir.

  1. A principios de C++, no había bool escribe. Entonces, el código que quería probar la confiabilidad necesitaba usar el !! modismo

    class uint256;  // Very wide integer
    uint256 x;
    
    // problem  as (int)x may return just the lower bits of x
    if (x) 
    
    // fix
    if (!!x) 
    
  2. ¿Qué pasa con C++ hoy (sé que esta es una pregunta de C) cuando no hay (bool) definido por el operador, no es el (int) operador utilizado? Esto da como resultado el mismo problema que el #2. Como en muchos de los primeros años, las bases de código C y C++ se mantuvieron sincronizadas, usando !! tenía relevancia con constructos como if (!!x).


Usando !! funciona hoy, pero ciertamente ha caído en desgracia, ya que resuelve un problema que ya no ocurre con una frecuencia significativa.

  • !! todavía se usa para cumplir con C89 al pasar el resultado de la prueba a una función

    –MM

    25 de febrero de 2016 a las 0:38

  • @MM De acuerdo, esa es una aplicación útil de !!. La publicación, sin embargo, se centra en el uso con if().

    – chux – Reincorporar a Monica

    25 de febrero de 2016 a las 1:57

  • !! todavía se usa para cumplir con C89 al pasar el resultado de la prueba a una función

    –MM

    25 de febrero de 2016 a las 0:38

  • @MM De acuerdo, esa es una aplicación útil de !!. La publicación, sin embargo, se centra en el uso con if().

    – chux – Reincorporar a Monica

    25 de febrero de 2016 a las 1:57

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad