Какова цель блока 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).

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

Ответы:

92

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

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

if (0) {
    // Empty braces
} else 

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

PSkocik
источник
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) {}часть. Простой автогенератор мог бы сгенерировать такой код, так как его немного проще кодировать - ему не нужно отдельно рассматривать первый включенный блок.

CSM
источник
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.)

Studog
источник
9

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

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

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

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

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

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

Simonarame
источник
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
Philfr
источник
6

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

https://godbolt.org/z/PETIks

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

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

cha0site
источник
6

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

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

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

стал

if (0) {
   // empty
} else if (some_fn()) {
   ...
sergiopm
источник
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)несколько ленивый способ удалить все, что там было .

Джон Ю
источник