Какова цель блока if (0) в блоке if-else?


142

Мой вопрос касается строки, которую я упомянул в теме и которую я вижу во многих местах внутри производственного кода.

Общий код выглядит так:

if (0) {
    // Empty braces
} else if (some_fn_call()) {
    // actual code
} else if (some_other_fn_call()) {
    // another actual code
    ...
} else {
    // default case
}

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

Я пытался найти этот явный случай здесь, в SO и в Интернете, но безуспешно. Там вы подобные вопросы о JavaScript, но не C. Там другой вопрос, что происходит , когда ноль назначается в `состоянии if`? , но обсуждается нулевое присвоение переменной, а не само использование if (0).


2
Это утверждение кажется неуместным. Сгенерируйте ассемблерный код с этим оператором и без него, и вы увидите, что происходит под капотом.
— haccks

2
Возможно, это автоматически сгенерированный код.
— freakish

Ответы:


92

Я иногда использую это для симметрии, чтобы я мог else if{свободно перемещать другой с помощью своего редактора, не обращая внимания на первое if.

Семантически

if (0) {
    // Empty braces
} else 

part ничего не делает, и вы можете рассчитывать на то, что оптимизаторы удалят его.


239
Личное мнение: хотя это может быть причиной того, почему он написан так, как есть, я думаю, что это плохое оправдание. Код читается чаще, чем пишется, и этот ненужный код просто увеличивает накладные расходы на синтаксический анализ для читателя.
— user694733

13
@ user694733: Вы можете возразить, что общий if elseпрефикс для всех значимых путей кода хорошо выравнивает условия и упрощает их сканирование. (Однако это субъективно и во многом будет зависеть от того, что на самом деле находится внутри условий и блоков кода.)
— M Oehm

72
Я не думаю, что это if (0) {..}вызывает какие-либо проблемы с анализируемостью / читабельностью. Это должно быть очевидно для любого, кто немного знает C. Это не проблема. Проблема в том, что после прочтения можно задать следующий вопрос: «Какого черта это тогда?» Если это не для отладочных / временных целей (т.е. намерение состоит в том, чтобы «включить» этот ifблок позже), я бы рекомендовал удалить его полностью. Обычно «чтение» такого кода может вызвать ненужную «паузу» для читателя без уважительной причины. И это достаточно веская причина, чтобы его удалить.
— PP

77
Похоже, это определенно ухудшает читабельность. Это было настолько плохо, что он послал этого программиста в SO, чтобы спросить, для чего это было. Плохой знак.
— Vectorjohn

26
Даже используя этот шаблон, я не знаю, можете ли вы «перемещаться else ifпо редактору без беспокойства», потому что условия могут не быть взаимоисключающими, и в этом случае порядок имеет значение. Лично я бы использовал только if, и выполнял бы ранний возврат , при необходимости выделяя логическую цепочку в отдельную функцию.
— Джон Ву

105

Это может быть полезно, если есть #ifутверждения, аля

   if (0)
   {
       // Empty block
   }
#if TEST1_ENABLED
   else if (test1())
   {
      action1();
   }
#endif
#if TEST2_ENABLED
   else if (test2())
   {
      action2();
   }
#endif

и т.п.

В этом случае любые (и все) тесты могут быть #ifисключены, и код будет правильно скомпилирован. Почти все компиляторы удаляют эту if (0) {}часть. Простой автогенератор мог бы сгенерировать такой код, так как его немного проще кодировать - ему не нужно отдельно рассматривать первый включенный блок.


5
Во многих случаях цепочка if/ else ifиспользуется не столько как дерево решений, сколько как конструкция «действие при первом совпадающем условии», где условие, которое имеет наивысший приоритет, не является особенно «особенным». Хотя я не видел, чтобы if(0)этот метод позволял всем реальным ветвям иметь согласованный синтаксис, мне нравится согласованный синтаксис, который он обеспечивает.
— supercat

1
В данном случае это даже бесполезно, потому что вы можете добиться того же эффекта без: просто разделите else ifстроку на две и поместите между ними защиту препроцессора.
— Конрад Рудольф

1
@KonradRudolph Я не слежу; как бы вы это написали?
— Джик

1
@JiK Я бы удалил if (0)ветку и переформатировал остальное так, чтобы elseоно находилось на отдельной строке, окруженное охраной по строкам #if TEST1_ENABLED && TEST2_ENABLED.
— Конрад Рудольф

5
@KonradRudolph, это нормально, если вы хотите удвоить количество охранников и утроить количество упомянутых условий охраны, я полагаю.
— hobbs

45

Я видел похожий шаблон, используемый в сгенерированном коде. Например, в SQL я видел, как библиотеки выдавали следующее whereпредложение.

where 1 = 1

По-видимому, это упрощает добавление других критериев, потому что все дополнительные критерии могут быть добавлены с помощью and вместо дополнительной проверки, чтобы увидеть, является ли это первым критерием или нет.


4
Это 1=1также «полезно», потому что вы всегда можете добавить whereвпереди без каких-либо условий. В противном случае вам придется проверить, пусто ли оно, и если да, то избегайте создания whereпредложения.
— Bakuriu

2
Кроме того, большинство баз данных будет автоматически «удалить» 1=1из WHERE, так что это не влияет на производительность.
— Иск Фонда Моники

7
Это допустимо в библиотеке, которая автоматически генерирует SQL-запросы, которые, скорее всего, никогда не увидит даже команда DevOps. Это неприемлемо для высокоуровневого кода, который приходится писать и читать несколько раз.
— phagio

Это действительно удобный подход при генерации какого-то динамического SQL с неизвестным количеством конечных условий.
— Skipper

1
@freakish на самом деле я написал обратное: плохо читаемый синтаксис приемлем в сгенерированном коде, поскольку он, скорее всего, никогда не будет прочитан, а не в функциональном коде высокого уровня, который поддерживается разработчиками.
— phagio

44

Как написано, if (0) {} предложение ни к чему не приводит.

Я подозреваю, что функция предложения наверху этой лестницы - предоставить удобное место для одновременного временного отключения всех других функций (для целей отладки или сравнения), изменив значение 0на 1или true.


2
Успешно справился. Я не видел другой причины, кроме отладки.
— tfont

16

Я не уверен в оптимизации, но мои два цента:

Это произошло из-за некоторой модификации кода, когда было удалено одно основное условие ( ifскажем, вызов функции в начальном блоке), но разработчики / сопровождающие

поэтому вместо удаления связанного ifблока они просто изменили условие на if(0)и двинулись дальше.


3
Разве if(0)покрытие филиалов тоже не уменьшается?
— Дэвид Салаи

1
@DavidSzalai Не полностью - максимум уменьшится на 1 (по сравнению с предыдущими 2) - но, насколько мне известно, для покрытия все равно потребуется один удар.
— Sourav Ghosh

15

Это кодовая гниль.

В какой-то момент это «если» сделало что-то полезное, ситуация изменилась, возможно, оцениваемая переменная была удалена.

Человек, который исправлял / изменял систему, как можно меньше влиял на логику системы. поэтому он просто удостоверился, что код будет перекомпилирован. Поэтому он оставляет «if (0)», потому что это быстро и легко, и он не совсем уверен, что он хочет делать. Он заставляет систему работать и не возвращается, чтобы полностью ее исправить.

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


2
Ага. Для древнего кода вносите одно изменение, удаляющее мертвый код, за раз. Я не могу сосчитать, сколько раз я впадал в ярость против «мертвого» кода только для того, чтобы обнаружить какой-то странный побочный эффект, который упускал из виду.
— Джули в Остине

15

Одна возможность, о которой еще не упоминалось: if (0) {строка может служить удобным местом для точки останова.

Отладка часто выполняется на неоптимизированном коде, поэтому будет присутствовать тест «всегда ложь» и на нем можно будет установить точку останова. При компиляции для производства строка кода будет оптимизирована. Эта на первый взгляд бесполезная строка предоставляет функциональные возможности для разработки и тестирования сборок, не влияя на сборку выпуска.

Выше есть и другие хорошие предложения; единственный способ действительно узнать, какова цель, - это найти автора и спросить. Ваша система управления исходным кодом может помочь в этом. (Ищите blameфункциональные возможности -type.)


9

Я видел недостижимые блоки кода в предварительно расширенном JavaScript, которые были созданы с использованием языка шаблонов.

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

if ( ${requestIsNotHttps} ){ ... }else if( ...

который когда-то предварительно скомпилирован отсюда:

if ( 0 ){ ... }else if ( ...

Надеюсь, это поможет вам релятивизировать потенциально низкую активность клавиатуры в эпоху сторонников утилизации кодировщиков, к которой я проявляю энтузиазм!


1
Я согласен, в эпоху повсеместной автоматизации нам следует больше полагаться на автогенерируемый код, поскольку он позволяет нам тратить больше времени на реальные вещи. Но сейчас меня интересует, как все это устроено под капотом.
— Zzaponka

8

Эта конструкция также может использоваться в C для реализации общего программирования с безопасностью типов, полагаясь на тот факт, что недоступный код все еще проверяется компилятором:

// this is a generic unsafe function, that will call fun(arg) at a later time
void defer(void *fun, void *arg);

// this is a macro that makes it safer, by checking the argument
// matches the function signature
#define DEFER(f, arg) \
   if(0) f(arg); \              // never actually called, but compile-time checked
   else defer(f, (void *)arg);  // do the unsafe call after safety check

void myfunction(int *p);

DEFER(myfunction, 42);     // compile error
int *b;
DEFER(myfunction, b);      // compiles OK

6

Я думаю, это просто плохой код. Написав быстрый пример в Compiler Explorer, мы видим, что и в gcc, и в clang не создается код дляif (0) блока , даже при полностью отключенной оптимизации:

https://godbolt.org/z/PETIks

Игра с устранением if (0)причин не меняет сгенерированный код, поэтому я прихожу к выводу, что это не оптимизация.

Возможно, что раньше в верхнем ifблоке было что-то, что потом убрали. Короче говоря, похоже, что его удаление приведет к генерации того же самого кода, так что не стесняйтесь делать это.


6

Как было сказано, ноль оценивается как ложь, и ветвь, вероятно, будет оптимизирована компилятором.

Я также видел это раньше в коде, где была добавлена ​​новая функция и требовался аварийный переключатель (если что-то пойдет не так с функцией, вы можете просто отключить ее), а через некоторое время, когда переключатель был удален, программист также не удалил ветку, например

if (feature_a_active()) {
    use_feature_a();
} else if (some_fn()) {
   ...

стал

if (0) {
   // empty
} else if (some_fn()) {
   ...

1

Это помогает отладить этот блок, просто поместив блок if 1. Это отключит все функции блока if else. А также мы можем расширить блок if else.


1
    Actually according to my opinion, if we put any variable for checking inside
    e.g:-
public static void main(string args[])
{
        var status;
        var empList=_unitofWork.EmpRepository.Get(con=>con.isRetired==true);
        //some code logic 
        if(empList.count>0)
        {
          status=true;
        }
        if(status)
        {
         //do something
        }
        else
        {
        //do something else
        }
}
     if then its dynamically get the value in run time and invoke the logic inside it, else its simply extra line of code i guess.

    Anybody have any depth knowledge why this thing is used....or agree with me.
    kindly respond. 

1

@ PSkocik ответ в порядке, но я добавляю свои два цента. Не уверен, следует ли мне сделать это как комментарий или как ответ; выбирая последнее, потому что ИМХО стоит того, чтобы другие посмотрели, а комментарии часто не видны.

Я не только иногда использую

if(0) {
   //deliberately left empty
} else if( cond1 ) {
   //deliberately left empty
} else if( cond2 ) {
   //deliberately left empty
...
} else {
   // no conditions matched
}

Но я также иногда делаю

if( 1 
    && cond1 
    && cond2
    ...
    && condN
) {

или

if( 0 
    || cond1 
    || cond2
    ...
    || condN
) {

для сложных условий. По тем же причинам - проще редактировать, #ifdef и т. Д.

Если на то пошло, в Perl я сделаю

@array = (  
    elem1,
    elem2,
    ...
    elem1,
) {
  • обратите внимание на запятую в конце списка. Я забываю, являются ли запятые разделителями или разделителями в списках C и C ++. ИМХО, это одна вещь, которую мы узнали: [ Заключительные запятые в Perl - плохая практика? запятые] - это хорошо. Как и к любым новым обозначениям, нужно время, чтобы к ним привыкнуть.

Я сравниваю if(0)код с лиспом

(cond   (test1    action1)
   (test2    action2)
   ...
   (testn   actionn))

который, как вы уже догадались, я могу обозначить как

(cond   
   (test1    action1)
   (test2    action2)
   ...
   (testn   actionn)
)

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

Возможно

IF
:: cond1 THEN code1
:: cond2 THEN code2
...
:: condN THEN codeN
FI

Вдохновленный [ https://en.wikipedia.org/wiki/Guarded_Command_Language#Selection:_if provided[Guarded Command Language] Дикстры ).

Но этот синтаксис подразумевает, что условия оцениваются параллельно, тогда как if...else-ifподразумевает последовательную и приоритетную оценку условий.

Я начал делать такие вещи, когда писал программы, генерирующие другие программы, где это особенно удобно.

Пока мы пишем RTL с использованием старого iHDL Intel, я закодировал такие вещи, как

   IF 0 THEN /*nothing*/
   **FORC i FROM 1 TO 10 DOC** 
   ELSE IF signal%i% THEN    
      // stuff to do if signal%i% is active
   **ENDC** 
   ELSE   
      // nothing matched 
   ENDIF

где FORC..DOC..ENDC- конструкция цикла макрокоманды препроцессора, которая расширяется до

   IF 0 THEN /*nothing*/
   ELSE IF signal1 THEN    
      // stuff to do if signal1 is active
   ELSE IF signal2 THEN    
      // stuff to do if signal2 is active
   ...
   ELSE IF signal100 THEN    
      // stuff to do if signal100 is active
   ELSE   
      // nothing matched 
   ENDIF

Это был код с однократным присваиванием, не обязательный, поэтому установка переменной состояния была запрещена, если вам нужно было сделать что-то вроде поиска первого установленного бита.

   IF 0 THEN /*nothing*/
   ELSE IF signal1 THEN    
      found := 1
   ELSE IF signal2 THEN    
      found := 2
   ...
   ELSE IF signal100 THEN    
      found := 100
   ELSE   
      // nothing matched 
   ENDIF

Если подумать, возможно, я впервые столкнулся с подобными конструкциями.

Кстати, возражения, которые некоторые высказывали против стиля if (0) - что условия else-if последовательно зависят и не могут быть произвольно переупорядочены - не относятся к логике AND, OR и XOR в RTL - но относятся к коротким- схема && и ||.


-1

Я видел, как это используется для обработки ошибок, например

if(0){
lable1:
   //do something
}
if(0){
lable2:
   //do something
}
.
.
and so on.

if(condition_fails)
   goto lable1;

Это может быть полезно, когда goto используется для управления ошибками, операторы выполняются только при возникновении ошибки. Я видел это в очень старом C-коде (где аргументы функций написаны вне '()'), не думайте, что сейчас кто-то следит за этим.


-2

Я видел это несколько раз, я думаю, что наиболее вероятная причина в том, что он оценивал что-то в более старой / другой версии / ветке кода или, возможно, для отладки, и изменение его на if(0)несколько ленивый способ удалить все, что там было .

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.