В чем преимущество прекращения 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
утверждение-это оборонительное Программирование. Theelse
заявление либо принять соответствующие меры или дать соответствующий комментарий относительно того, почему нет принимаются меры. Это соответствует требованию иметь финал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, если это может снизить надежность для времени загрузки сервера, чтобы проверить все дополнительный код.