使用if(!!(expr))而不是if(expr)

时间:2016-02-20 12:08:04

标签: c expression

在阅读德州仪器为其SensorTag提供的示例代码时,我遇到了以下代码段。

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);
    }
    ...
}

声明就像这样(在同一个文件中)。

#define IO_DATA_LED1   0x01
static uint8_t ioValue;

if (!!(ioValue & IO_DATA_LED1))提供优于if (ioValue & IO_DATA_LED1)的优势吗?

5 个答案:

答案 0 :(得分:79)

两次应用逻辑非($params = $this->model->get(); foreach($params as $value) $new_val = explode(',',$value->arg_1); { echo form_checkbox($new_val); } )运算符的目的是将值规范化为0或1.在if语句的控制表达式中,这没有任何区别。 if语句只关心值为零或非零,小!舞是完全没用的。

某些编码风格指南可能会强制执行此类舞蹈,这可能是您发布的TI代码执行此操作的原因。我没有看到任何这样做。

答案 1 :(得分:54)

如果!!x是真值(非零数字或非空指针),则表达式!(!x)x表示1,否则为0.它相当于{{1它与C99 x != 0几乎相同,但在C99之前或其开发人员选择不实现C99的编译器中可用(例如cc65以MOS 6502为目标)。

整个条件相当于以下内容:

(_Bool)x

在C中,它表示“如果这两个值的按位AND非零,则执行该块。”

但是某些编码样式指南可能会禁止在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 */ } 语句条件的顶层使用按位AND(&),假设它是逻辑AND(if的拼写错误)。它与使用&&(赋值)而不是=(相等比较)在同一类错误中,许多编译器为此提供诊断。 GCC Warning Options描述了这样的诊断:

  

==:警告表达式中可疑的逻辑运算符用法。这包括在可能需要逐位运算符的上下文中使用逻辑运算符。

     

-Wlogical-op:如果在某些上下文中省略括号,则发出警告,例如在预期真值的上下文中有赋值

使用诸如-Wparentheses(a & B) != 0(_Bool)(a & B)之类的释义与编译器以及使用按位运算符的其他开发人员进行通信是有意的。

另请参阅有关!!x in JavaScript的相关答案。

答案 2 :(得分:16)

在MSVC中,bool语句中隐式地将整数转换为if可以生成警告。通过!!这样做不会。其他编译器可能存在类似的警告。

假设代码是在启用该警告的情况下编译的,并且决定将所有警告视为错误,使用!!是一种简单易用的方式来说“是的,我希望这个整数是{{ 1}}”。

答案 3 :(得分:4)

虽然最有可能使编译器警告位为import tkinter import importlib import main class Executor: def __init__(self): self.root = tkinter.Tk() self.app = main.Application(master=self.root, on_reload=self.on_reload) self.app.mainloop() def on_reload(self): self.root.destroy() importlib.reload(main) self.root = tkinter.Tk() self.app = main.Application(master=self.root, on_reload=self.on_reload) self.app.mainloop() if __name__ == '__main__': Executor() ,但这看起来似乎也可能是为了可读性而添加枚举的重构结果:

&

为:

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

因为那超过了80个字符的限制,所以它被重构为

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);

我个人更喜欢初始版本的可读性,但是当代码行被用作度量时,这个版本很常见(我很惊讶它没有为每个状态声明变量,分别设置每个状态然后使用这一点)。

此“最佳实践”代码的下一个版本可能如下所示:

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);
}

所有这些都可以这样做:

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

答案 4 :(得分:3)

  OP正在研究一些旧的编码习惯用法 - 这对BITD来说是有道理的(在当天)。

  1. !!的主要用途是处理将if(expr)中的表达式转换为int而不是针对零进行测试的C实现。
  2. 考虑当expr转换为int然后针对0进行测试时会发生什么。(因为C89,这是不符合的,因为测试应该是针对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)
    

    因此,对于C89之前的编译器以及不符合标准的C89及更高版本,使用!!应对该弱点。一些旧习惯需要很长时间才能死去。

    1. early C++中,没有bool类型。因此,想要测试可信度的代码需要使用!!成语

      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. 当没有(bool)运算符定义,使用(int)运算符时,今天C ++会发生什么(我知道这是一个C问题)?这导致与#2相同的问题。至于许多早期,C和C ++代码库保持同步,使用!!if (!!x)等结构相关。

    3. 今天使用!!有效,但肯定已经失宠了,因为它解决了任何重要频率都不再发生的问题。