В чем преимущество прекращения if ... else if конструкций с предложением else?

Наша организация имеет требуются правило кодирования (без каких-либо объяснений), что:

if ... else if конструкции должны быть завершены предложением else

Пример 1:

if ( x < 0 )
{
   x = 0;
} /* else not needed */

Пример 2:

if ( x < 0 )
{
    x = 0;
}
else if ( y < 0 )
{
    x = 3;
}
else    /* this else clause is required, even if the */
{       /* programmer expects this will never be reached */
        /* no change in value of x */
}

какой край случае это предназначено для обработки?

что также беспокоит меня по поводу причины, так это то, что Пример 1 не нужно else но Пример 2 делает. Если причина в повторном использовании и расширяемости, я думаю else следует использовать в обоих случаях.

12 ответов


как упоминалось в другом ответе, это из руководящих принципов кодирования MISRA-C. Цель-оборонительное Программирование, концепция, которая часто используется в критически важном программировании.

то есть, каждый if - else if должно заканчиваться на else, и все switch должен заканчиваться на default.

для этого есть две причины:

  • самодокументированный код. Если вы напишете else но оставьте его пустым, это означает: "Я определенно рассматривался сценарий, когда ни if, ни else if истинны".

    не пишу else там означает: "либо я рассматривал сценарий, где ни if, ни else if верны, или я полностью забыл рассмотреть это, и в моем коде потенциально есть жирная ошибка".

  • остановить убегающих код. В критически важном программном обеспечении вам нужно написать надежные программы, которые учитывают даже маловероятное. Таким образом, вы могли видеть код, как

    if (mybool == TRUE) 
    {
    } 
    else if (mybool == FALSE) 
    {
    }
    else
    {
      // handle error
    }
    

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

    исторически, вы боялись бы повреждения памяти RAM из-за EMI/шума. Сегодня это не такая уж большая проблема. Гораздо более вероятно, повреждение памяти происходит из-за ошибок в другом месте кода: указатели на неправильные местоположения, ошибки array-out-of-bounds, переполнение стека, беглый код и т. д.

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


редактировать

относительно того, почему else не требуется после каждого if:

An if-else или if-else if-else полностью охватывает все возможные значения, которые может иметь переменная. Но простой if оператор не обязательно должен охватывать все возможные значения, он имеет гораздо более широкое использование. Чаще всего вы просто хотите проверить определенное условие и если оно не выполняется, то ничего не делать. Тогда просто не имеет смысла писать оборонительное программирование для покрытия else случае.

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

MISRA-C: 2012 15.7 не дает никакого обоснования, почему else не требуется, он просто заявляет:

Примечание: заключительный else заявление не требуется для простого if заявление.


ваша компания следовала руководству кодирования MISRA. Существует несколько версий этих рекомендаций, содержащих это правило, но из MISRA-C:2004:

правило 14.10 (обязательно): все, если ... иначе, если конструкции должны быть прекращены с другого пункта.

это правило применяется всякий раз, когда оператор if сопровождается одним или несколькими еще если утверждения; окончательное еще if следует за else заявление. В случай простой if инструкция тут else заявление включать не нужно. Требование для окончательного else утверждение-это оборонительное Программирование. The else заявление либо принять соответствующие меры или дать соответствующий комментарий относительно того, почему нет принимаются меры. Это соответствует требованию иметь финал default п. в switch заявление. Например этот код простое утверждение if:

if ( x < 0 )
{
 log_error(3);
 x = 0;
} /* else not needed */

в то время как следующий код демонстрирует if, else if построить

if ( x < 0 )
{
 log_error(3);
 x = 0;
}
else if ( y < 0 )
{
 x = 3;
}
else /* this else clause is required, even if the */
{ /* programmer expects this will never be reached */
 /* no change in value of x */
}

на MISRA-C: 2012, который заменяет версию 2004 года и является текущей рекомендацией для новых проектов,то же самое правило существует, но имеет номер 15.7.

Пример 1: в одном операторе if программисту может потребоваться проверить n условий и выполнить одну операцию.

if(condition_1 || condition_2 || ... condition_n)
{
   //operation_1
}

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

Пример 2: Здесь программист проверяет n количество условий и выполняет несколько операций. В регулярном использовании if..else if как switch возможно, Вам потребуется выполнить операцию по умолчанию. Так использование else необходимо согласно стандарту misra

if(condition_1 || condition_2 || ... condition_n)
{
   //operation_1
}
else if(condition_1 || condition_2 || ... condition_n)
{
  //operation_2
}
....
else
{
   //default cause
}

текущие и прошлые версии этих публикаций доступны для покупки через Мишра интернет-магазин ( via).


это эквивалентно требованию случая по умолчанию в каждом коммутаторе.

этот дополнительный еще уменьшится покрытия кода программы.


в моем опыте с портированием ядра linux или кода android на другую платформу много раз мы делаем что-то неправильно, и в logcat мы видим некоторую ошибку, такую как

if ( x < 0 )
{
    x = 0;
}
else if ( y < 0 )
{
    x = 3;
}
else    /* this else clause is required, even if the */
{       /* programmer expects this will never be reached */
        /* no change in value of x */
        printk(" \n [function or module name]: this should never happen \n");

        /* It is always good to mention function/module name with the 
           logs. If you end up with "this should never happen" message
           and the same message is used in many places in the software
           it will be hard to track/debug.
        */
}

только краткое объяснение, так как я сделал это все около 5 лет назад.

нет (с большинством языков) синтаксического требования включать "null" else заявление (и ненужно {..}), а в "простых маленьких программах" нет необходимости. Но настоящие программисты не пишут "простых маленьких программ", и, что не менее важно, они не пишут программ, которые будут использоваться один раз, а затем отброшены.

когда один пишет if / else:

if(something)
  doSomething;
else
  doSomethingElse;

все это кажется простым, и едва ли можно увидеть даже смысл добавления {..}.

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

if(something)
  doSomething;
else
  doSomethingIForgot;
  doSomethingElse;

вдруг doSomethingElse как бы забывает, что он должен быть в else нога.

Итак, вы хороший маленький программист, и вы всегда используете {..}. Но вы пиши:

if(something) {
  if(anotherThing) {
    doSomething;
  }
}

все хорошо и хорошо, пока этот новый ребенок не сделает полуночную модификацию:

if(something) {
  if(!notMyThing) {
  if(anotherThing) {
    doSomething;
  }
  else {
    dontDoAnything;  // Because it's not my thing.
  }}
}

Да, он неправильно отформатирован, но так же половина кода в проекте, и "автоматический форматер" получает болликс от всех #ifdef заявления. И, конечно же, реальный код намного сложнее, чем этот игрушечный пример.

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


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

Это хорошая практика программирования, который делает код многоразовые и extend-able.


Я хотел бы добавить – и частично противоречить-предыдущие ответы. Хотя, конечно, часто используется if-else if в переключателе, который должен охватывать весь диапазон мыслимых значений для выражения, ни в коем случае не гарантируется, что любой диапазон возможных условий полностью охвачен. То же самое можно сказать о самой конструкции коммутатора, следовательно, требование использовать предложение по умолчанию, которое улавливает все оставшиеся значения и может, если не требуется иначе, использоваться как доказательство защиты.

сам вопрос имеет хороший встречный пример: второе условие вообще не относится к x (именно поэтому я часто предпочитаю более гибкий вариант на основе if над вариантом на основе switch). Из примера очевидно, что если условие A выполнено, x должно быть установлено в определенное значение. Если A не выполняется, проверяется условие B. Если оно выполнено, то x должно получить другое значение. Если ни A, ни B не выполняются, то x должен оставаться не менявшийся.

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

в C нет такой вещи, как "еще если". Есть только if и else. вместо этого, согласно МИСРЕ, конструкция должна быть формально отступом таким образом (и я должен был поставить открывающие фигурные скобки на свои строчки, но мне это не нравится):
if (A) {
    // do something
}
else {
    if (B) {
        // do something else (no pun intended)
    }
    else {
        // don't do anything here
    }
}

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

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

if (A) {
    if (B) {
        // do something
    }
    // you could to something here
}
else {
    // or here
    if (B) { // or C?
        // do something else (no pun intended)
    }
    else {
        // don't do anything here, if you don't want to
    }
    // what if I wanted to do something here? I need brackets for that.
}

в конце концов, для них это сводится к точному определению того, что подразумевается под "Если"... иначе если построить"


основной причиной, вероятно, является покрытие кода и неявное else: как будет вести себя код, если условие неверно? Для подлинного тестирования вам нужно каким-то образом увидеть, что вы протестировали с условием false. Если каждый тестовый случай проходит через предложение if, ваш код может иметь проблемы в реальном мире из-за условия, которое вы не тестировали.

однако некоторые условия могут быть подобны примеру 1, например, в налоговой декларации: "если результат меньше, чем 0, введите 0."Вам все равно нужно пройти тест, где условие ложно.


логически любой тест подразумевает две ветви. Что делать, если это правда, и что делать, если это ложь.

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

Это может быть полезно для следующего программиста обслуживания, который придет. Им не нужно искать слишком далеко, чтобы решить, правильный ли код. Вы можете своего рода Prehunt в Слон.

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

ваш пробег может варьироваться.


в большинстве случаев, когда у вас есть только один if заявление, это, вероятно, одна из причин, таких как:

  • функция guard проверяет
  • опции инициализации
  • дополнительные обработки филиала

пример

void print (char * text)
{
    if (text == null) return; // guard check

    printf(text);
}

но когда вы делаете if .. else if, Это, наверное, одна из причин таких как:

  • динамический переключатель-case
  • обработка вилка
  • обработка параметра обработки

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

пример

int absolute_value (int n)
{
    if (n == 0)
    {
        return 0;
    }
    else if (n > 0)
    {
        return n;
    }
    else /* if (n < 0) */ // redundant check
    {
        return (n * (-1));
    }
}

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

пример

#DEFINE SQRT_TWO   1.41421356237309504880
#DEFINE SQRT_THREE 1.73205080756887729352
#DEFINE SQRT_FIVE  2.23606797749978969641

double square_root (int n)
{
         if (n  > 5)   return sqrt((double)n);
    else if (n == 5)   return SQRT_FIVE;
    else if (n == 4)   return 2.0;
    else if (n == 3)   return SQRT_THREE;
    else if (n == 2)   return SQRT_TWO;
    else if (n == 1)   return 1.0;
    else if (n == 0)   return 0.0;
    else               return sqrt(-1); // error handling
}

этот заключительный else статья очень похожа на несколько других вещей, в таких языках, как Java и C++, например:

  • default case в операторе switch
  • catch(...) что приходит после всего конкретного catch блоки
  • finally в предложении try-catch

наше программное обеспечение не было критически важным, но мы также решили использовать это правило из-за оборонительного программирования. Мы добавили исключение throw к теоретически недостижимому коду (switch + if-else). И это спасло нас много раз, поскольку программное обеспечение быстро отказало, например, когда был добавлен новый тип, и мы забыли изменить один или два if-else или switch. В качестве бонуса это сделало супер легко найти проблему.


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

int a = 0;
bool b = true;
uint8_t* bPtr = (uint8_t*)&b;
*bPtr = 0xCC;
if(b == true)
{
    a += 3;
}
else if(b == false)
{
    a += 5;
}
else
{
    exit(3);
}

вы, вероятно, никогда не ожидали бы bool это не true, ни false, как бы то ни было. Лично я считаю, что это проблема, вызванная человеком, который решает сделать что-то необычное, но дополнительное else утверждение может предотвратить дальнейшие проблемы.


в настоящее время я работаю с PHP. Создание регистрационной формы и формы входа. Я просто использую if и else. Нет больше, если или что-то, что не нужно.

Если пользователь нажимает кнопку отправки - > он переходит к следующему оператору if... если имя пользователя меньше, чем " X " количество символов, то предупреждение. Если успешно, то проверьте длину пароля и так далее.

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