В чем преимущество прекращения if... else if constructs с предложением else?
Наша организация имеет обязательное правило кодирования (без каких-либо объяснений), которое:
if... else, если конструкции должны быть прерваны с помощью предложения 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
должен использоваться в обоих случаях.
Ответы
Ответ 1
Как уже упоминалось в другом ответе, это из правил кодирования 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/noise. Сегодня это не большая проблема. Скорее всего, повреждение памяти происходит из-за ошибок в другом месте кода: указатели на неправильные местоположения, ошибки из-за границ, переполнение стека, код убегания и т.д.
Поэтому большую часть времени такой код возвращается, чтобы ударить себя по лицу, когда вы написали ошибки на этапе внедрения. Это также можно использовать в качестве метода отладки: программа, которую вы пишете, сообщает вам, когда вы писали ошибки.
ИЗМЕНИТЬ
Относительно того, почему else
не требуется после каждого if
:
An if-else
или if-else if-else
полностью охватывает все возможные значения, которые может иметь переменная. Но простой if
оператор не обязательно должен охватывать все возможные значения, он имеет гораздо более широкое использование. Чаще всего вы просто хотите проверить определенное состояние, и если оно не выполнено, тогда ничего не делайте. Тогда просто не имеет смысла писать защитное программирование для покрытия случая else
.
Плюс это полностью загромождает код, если вы написали пустой else
после каждого if
.
MISRA-C: 2012 15.7 не дает никакого объяснения, почему else
не требуется, он просто устанавливает:
Примечание: окончательный оператор else
не требуется для простого if
утверждение.
Ответ 2
Ваша компания следила за инструкциями по кодированию MISRA. Есть несколько версий этих рекомендаций, которые содержат это правило, но из MISRA-C: 2004 †:
Правило 14.10 (обязательно): Все, если... else, если конструкции должны быть завершены с предложением else.
Это правило применяется, когда за оператором if следует один или несколько else if statements; за последним if
следует else
выражение. В случае простой инструкции if
, тогда else
выражение не обязательно должно быть включено. Требование для окончательного else
утверждение - защитное программирование. Оператор else
должен либо принять соответствующие меры или содержать подходящий комментарий относительно того, почему нет предпринимается действие. Это согласуется с требованием иметь final 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:
в одном случае, если программисту-программисту может потребоваться проверить n количество условий и выполнить одну операцию.
if(condition_1 || condition_2 || ... condition_n)
{
//operation_1
}
При регулярном использовании выполнение операции не требуется все время, когда используется if
.
Пример 2:
Здесь программист проверяет число состояний и выполняет несколько операций. При регулярном использовании 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
}
† Текущие и прошлые версии этих публикаций доступны для покупки через веб-магазин MISRA (через).
Ответ 3
Это эквивалент требования по умолчанию для каждого коммутатора.
Это дополнительное дополнение уменьшит охват кода вашей программы.
В моем опыте портирования ядра 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.
*/
}
Ответ 4
Только краткое объяснение, так как я сделал это все около 5 лет назад.
Существует (с большинством языков) отсутствие синтаксического требования о включении оператора "null" else
(и ненужного {..}
), а в "простых маленьких программах" нет необходимости. Но настоящие программисты не пишут "простые маленькие программы", и, что не менее важно, они не пишут программы, которые будут использоваться один раз, а затем будут отброшены.
Когда вы пишете if 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 not my thing.
}}
}
Да, это неверно отформатировано, но так же половина кода в проекте, и "автоформат" становится взломанным всеми операторами #ifdef
. И, конечно же, реальный код намного сложнее, чем этот игрушечный пример.
К сожалению (или нет), я уже несколько лет не встречаюсь с этим, поэтому у меня нет нового "реального" примера - вышеупомянутое (очевидно) надуманное и бит hokey.
Ответ 5
Это делается для того, чтобы сделать код более удобочитаемым, для более поздних ссылок и дать понять, для более позднего рецензента, что остальные случаи, обработанные последним else
, ничего не делают чтобы они не упускались из виду как-то с первого взгляда.
Это хорошая практика программирования, которая делает код повторно используемым и расширяемым.
Ответ 6
Я хотел бы добавить к – и отчасти противоречат. предыдущие ответы. Хотя, безусловно, обычно использовать if-else, если с помощью переключателя, который должен охватывать весь спектр мыслимых значений для выражения, отнюдь не гарантируется, что любой диапазон возможных условий полностью покрыт. То же самое можно сказать и о самой конструкции коммутатора, поэтому требование использовать предложение по умолчанию, которое улавливает все оставшиеся значения и может, если вообще не требуется в любом случае, использоваться в качестве гарантии подтверждения.
Сам вопрос содержит хороший встречный пример: второе условие вообще не относится к x (именно по этой причине я часто предпочитаю более гибкий вариант if на основе варианта на основе коммутатора). Из примера видно, что если выполнено условие A, то x должно быть установлено на определенное значение. Если A не выполняется, тогда выполняется условие B. Если это выполняется, то x должно получить другое значение. Если ни A, ни B не выполнены, то x остается неизменным.
Здесь мы видим, что для комментария о намерении программиста для читателя следует использовать пустую ветку else.
С другой стороны, я не понимаю, почему должно быть предложение else специально для последнего и самого внутреннего оператора if. В C нет такой вещи, как "else if". Существует только if и else. Вместо этого, согласно MISRA, конструкцию следует формально отступать таким образом (и я должен был бы поместить открытые фигурные скобки в свои строки, но мне это не нравится):
if (A) {
// do something
}
else {
if (B) {
// do something else (no pun intended)
}
else {
// don't do anything here
}
}
Когда MISRA просит поместить фигурные скобки вокруг каждой ветки, тогда это противоречит самому себе, если упомянуть "if... else if constructs".
Любой может представить себе уродство глубоко вложенных, если еще деревьев, см. здесь на боковой ноте. Теперь представьте, что эта конструкция может произвольно расширяться в любом месте. Затем запрос предложения 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.
}
Таким образом, я уверен, что люди, которые разработали рекомендации MISRA, имели смену типа if-else, если у вас есть намерение.
В конце концов, это сводится к тому, что они точно определяют, что подразумевается под "if... else if construct"
Ответ 7
Логически любой тест подразумевает две ветки. Что вы делаете, если это правда, и что вы делаете, если оно ложно.
В тех случаях, когда ни одна из ветвей не имеет функциональности, разумно добавить комментарий о том, почему у нее нет функциональности.
Это может принести пользу следующему программисту-программисту. Им не нужно искать слишком далеко, чтобы определить, правильный ли код. Вы можете вроде Prehunt the Elephant.
Лично это помогает мне, поскольку это заставляет меня смотреть на случай другого и оценивать его. Это может быть невозможное условие, и в этом случае я могу сделать исключение, поскольку контракт нарушен. Это может быть доброкачественным, и в этом случае комментарий может быть достаточным.
Ваш пробег может отличаться.
Ответ 8
Основной причиной является, вероятно, покрытие кода и неявное else: как будет работать код, если условие не соответствует действительности? Для подлинного тестирования вам нужно каким-то образом убедиться, что вы протестировали с условием false. Если каждый тестовый пример проходит через предложение if, ваш код может иметь проблемы в реальном мире из-за условия, которое вы не тестировали.
Однако некоторые условия могут быть похожими на пример 1, например, на налоговую декларацию: "Если результат меньше 0, введите 0." Вы все равно должны иметь тест, в котором условие ложно.
Ответ 9
В большинстве случаев, когда у вас только один оператор if
, возможно, это одна из причин, например:
- Проверка функции
- Параметр инициализации
- Дополнительная ветка обработки
Пример
void print (char * text)
{
if (text == null) return; // guard check
printf(text);
}
Но когда вы делаете if .. else if
, это, вероятно, одна из причин, таких как:
- Динамический коммутационный футляр
- Обработка вилки
- Обработка параметра обработки
И в случае, если ваш 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
, обработка может быть выполнена через бизнес-уровень процедура, уведомление пользователя, механизм внутренней ошибки,..etc.
Пример
#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
Ответ 10
В настоящее время я работаю с PHP. Создание формы регистрации и формы входа. Я просто использую if и else. Нет другого, если это или что-то ненужное.
Если пользователь нажимает кнопку отправки → , он переходит к следующему оператору if... если имя пользователя меньше, чем количество символов "X", то предупреждение. В случае успеха проверьте длину пароля и т.д.
Нет необходимости в дополнительном коде, таком как else, если это может отбросить надежность для времени загрузки сервера, чтобы проверить весь дополнительный код.
Ответ 11
Наше программное обеспечение не было критически важным, но мы также решили использовать это правило из-за защитного программирования.
Мы добавили исключение броска к теоретически недостижимому коду (switch + if-else). И это спасло нас много раз, поскольку программное обеспечение быстро сработало, например. когда новый тип был добавлен, и мы забыли изменить один или два if-else или switch. В качестве бонуса было очень легко найти проблему.
Ответ 12
Ну, мой пример включает в себя поведение undefined, но иногда некоторые люди стараются быть фантастическими и терпят неудачу, посмотрите:
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
может помешать дальнейшим проблемам.