Используя if (!! (expr)) вместо if (expr)

Читая примерный код, предоставленный Texas Instruments для их SensorTag, я наткнулся на следующий fragment.

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

Применять логический не ( ! ) Оператор дважды имеет целью нормализовать значение как 0 или 1. В контрольном выражении if-statement это не имеет никакого значения. Оператор if только заботится о том, чтобы значение было нулевым или ненулевым, маленькое !! танец совершенно бесполезен.

Некоторые руководства по стилю кодирования могут назначать такой вид танца, что может быть причиной того, что код TI, который вы опубликовали, делает это. Я не видел никого, кто бы это сделал.

Выражение !!x или !(!x) означает 1, если x – истинное значение (ненулевое число или ненулевой указатель), в противном случае 0. Оно эквивалентно x != 0 , и это почти то же самое, что и C99 (_Bool)x но ansible в компиляторах, предшествовавших C99 или разработчики которых решили не внедрять C99 (например, cc65, который нацелен на MOS 6502).

Условие в целом эквивалентно следующему:

 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 */ } 

В C это означает «если побитовое И этих двух значений отличное от нуля, выполните блок».

Но некоторые руководства по стилю кодирования могут запрещать побитовое И ( & ) на верхнем уровне условия оператора if , предполагая, что это опечатка для логического И ( && ). Он находится в том же classе ошибок, что и использование = (присвоение) вместо == (сравнение равенства), для которого многие компиляторы предлагают диагностику. Параметры предупреждения GCC описывают такие функции, как:

-Wlogical-op : Предупреждать о подозрительном использовании логических операторов в выражениях. Это включает в себя использование логических операторов в контекстах, где вероятный битовый оператор может ожидаться.

-Wparentheses : Предупреждать, если круглые скобки опущены в определенных контекстах, например, когда есть назначение в контексте, где ожидается значение истины

Использование парафраза, такого как (a & B) != 0 , (_Bool)(a & B) или !!(a & B) связывается с компилятором и с другими разработчиками, которые используют побитовый оператор, были намеренными.

См. Также соответствующий ответ о !!x в JavaScript .

В MSVC преобразование целого числа в bool неявно в выражении if может генерировать предупреждение. Делать это через !! не. Подобное предупреждение может существовать и в других компиляторах.

Таким образом, предполагая, что код был скомпилирован с включенным предупреждением, и решение рассматривать все предупреждения как ошибки, используя !! это короткий и переносимый способ сказать «да, я хочу, чтобы это целое было bool ».

OP смотрит на какую-то старую кодирующую идиому, которая имела некоторый смысл BITD (назад в день).

  1. Основное использование !! должен был обрабатывать реализации C, которые преобразовывали выражение в if(expr) в int а не тестирование против нуля.

Рассмотрим, что произойдет, когда 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) 

Так что на pre C89 compliers и несоответствующих C89 и более поздних версиях, используя !! справился с этой слабостью. Некоторым старым привычкам требуется много времени, чтобы умереть.

  1. В начале 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. Что происходит с C ++ сегодня (я знаю, что это вопрос C), когда нет определенного оператора (bool) , не используется ли оператор (int) ? Это приводит к той же проблеме, что и # 2. Что касается многих ранних лет, базы кода C и C ++ сохранялись в синхронизации, используя !! имели отношение к конструкциям типа if (!!x) .


Использование !! работает сегодня, но, безусловно, вышла из неприязни, поскольку она решает проблему, которая больше не возникает с какой-либо значительной частотой.

Хотя глупое предупреждение о компиляторе для бит-мудрых & является наиболее вероятным, похоже, что это также может быть результатом рефакторинга для добавления перечислений для удобочитаемости:

 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 @MM points out 

чтобы:

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

Поскольку это превысило ограничение на 80 символов, оно было реорганизовано в

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

Лично я предпочел бы исходную версию для удобочитаемости, но эта версия распространена, когда строки кода используются как метрика (я удивлен, что он не объявлял переменные для каждого состояния, отдельно устанавливал каждое состояние, а затем использовал это).

Следующая версия этого кода «Best Practice» может выглядеть так:

 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 

Все это можно было бы сделать так:

 for (int i=0;i