66 votos

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

Al leer el código de ejemplo proporcionado por Texas Instruments para su SensorTag 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;

Hace if (!!(ioValue & IO_DATA_LED1)) ofrecen alguna ventaja sobre if (ioValue & IO_DATA_LED1) ?

77voto

FUZxxl Puntos 21462

Aplicando la lógica no ( ! ) tiene el propósito de normalizar un valor para que sea 0 o 1. En una expresión de control de una sentencia if, eso no supone ninguna diferencia. La declaración if sólo se preocupa de que el valor sea cero o distinto de cero, el pequeño !! la danza 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 que el código de TI que has publicado lo hace. Sin embargo, no he visto ninguna que lo haga.

54voto

Damian Yerrick Puntos 2451

La expresión !!x significa 1 si x es un valor verdadero (un número no nulo o un puntero no nulo), en caso contrario 0. Es equivalente a x != 0 o al C99 (_Bool)x pero disponible en compiladores anteriores a C99 o cuyos desarrolladores han decidido no implementar C99 (como cc65 que tiene como objetivo el MOS 6502).

El condicional en su conjunto equivale 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 a nivel de bits de esos dos valores es distinto de cero, ejecuta el bloque".

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

-Wlogical-op : Advierte 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 de tipo bit.

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

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

16voto

Yakk Puntos 31636

En MSVC la conversión de un entero a un bool implícitamente en un if puede generar una advertencia. Hacerlo a través de !! no lo hace. Una advertencia similar puede existir en otros compiladores.

Así que suponiendo que el código se compiló con esa advertencia activada, y una decisión de tratar todas las advertencias como errores, usando !! es una forma corta y portátil de decir "sí, quiero que este entero sea un bool ".

3voto

chux Puntos 13185

El operador está mirando un viejo lenguaje de codificación - que tenía algún sentido BITD (back-in-the-day).

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

Considere lo que sucede cuando expr se convierte en int y luego se prueba contra 0. (Desde C89, esto no es conforme ya que la prueba debería 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)

Por lo tanto, en los proveedores anteriores a C89 y en los no conformes con C89 y posteriores, el uso de !! se enfrentó a esa debilidad. Algunos viejos hábitos tardan mucho en morir.

  1. En principios de C++ No había ningún bool tipo. Por lo tanto, el código que quería probar la confianza necesitaba utilizar el tipo !! idioma

    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 en día (sé que es una pregunta de C) cuando no hay (bool) definido, no es el (int) operador utilizado? Esto resulta en el mismo problema que el #2. Como durante muchos años las bases de código C y C++ se mantuvieron sincronizadas, utilizando !! tenía relevancia con construcciones como if (!!x) .


Utilizando !! funciona hoy en día, pero ciertamente ha caído en desgracia, ya que resuelve un problema que ya no se produce con una frecuencia significativa.

3voto

technosaurus Puntos 1980

Aunque el silenciamiento de la advertencia del compilador para el bit-wise & es el más probable, esto parece que también podría ser el resultado de una refactorización para añadir enums para la legibilidad 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);

Como eso excedía el límite de 80 caracteres, se refactorizó a

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 se usan las líneas de código como métrica (me sorprende que no declarara variables para cada estado, estableciera cada estado por separado y luego usara eso).

La próxima versión de este código de "buenas prácticas" podría ser 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 podría haberse hecho así:

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

Iteramos.com

Iteramos es una comunidad de desarrolladores que busca expandir el conocimiento de la programación mas allá del inglés.
Tenemos una gran cantidad de contenido, y también puedes hacer tus propias preguntas o resolver las de los demás.

Powered by:

X