7.3 – Распространенные проблемы при работе с операторами if
Данный урок является продолжением урока «7.2 – Операторы if и блоки». Здесь мы рассмотрим некоторые типовые проблемы, возникающие при использовании операторов if
.
Вложенные операторы if
и проблема висячих else
Операторы if
можно вкладывать в другие операторы if
:
#include <iostream>
int main()
{
std::cout << "Enter a number: ";
int x{};
std::cin >> x;
if (x >= 10) // внешний оператор if
// это плохой стиль кодирования для вложенности операторов if
if (x <= 20) // внутренний оператор if
std::cout << x << "is between 10 and 20\n";
// к какому оператору if принадлежит этот else?
else
std::cout << x << "is greater than 20\n";
return 0;
}
Показанная выше программа вводит источник потенциальной неоднозначности, называемый проблемой висячих else
. Оператор else
в приведенной выше программе соответствует внешнему или внутреннему оператору if
?
Ответ заключается в том, что оператор else
объединяется с последним оператором if
, не имеющем else
, в том же блоке. Таким образом, в приведенной выше программе else
сопоставляется с внутренним оператором if
.
Чтобы избежать такой двусмысленности при вложении операторов if
, рекомендуется заключать внутренний оператор в блок. Вот переписанная программа без двусмысленности:
#include <iostream>
int main()
{
std::cout << "Enter a number: ";
int x{};
std::cin >> x;
if (x >= 10)
{
if (x <= 20)
std::cout << x << "is between 10 and 20\n";
else // прикреплен к внутреннему оператору if
std::cout << x << "is greater than 20\n";
}
return 0;
}
Теперь гораздо понятнее, что оператор else
принадлежит внутреннему оператору if
.
Заключение внутреннего оператора if
в блок также позволяет нам явно прикрепить else
к внешнему оператору if
:
#include <iostream>
int main()
{
std::cout << "Enter a number: ";
int x{};
std::cin >> x;
if (x >= 10)
{
if (x <= 20)
std::cout << x << "is between 10 and 20\n";
else // прикреплен к внутреннему оператору if
std::cout << x << "is greater than 20\n";
}
else // прикреплен к внешнему оператору if
std::cout << x << "is less than 10\n";
return 0;
}
Использование блока сообщает компилятору, что оператор else
должен прикрепляться к оператору if
перед блоком. Без блока оператор else
будет прикрепляться к ближайшему оператору if
, не имеющему else
, который будет внутренним оператором if
.
Разворачивание вложенных операторов if
Вложенные операторы if
часто можно развернуть либо путем реструктуризации логики, либо с помощью логических операторов (рассматривается в уроке «5.7 – Логические операторы»). Код с меньшей вложенностью менее подвержен ошибкам.
Например, приведенный выше пример можно развернуть следующим образом:
#include <iostream>
int main()
{
std::cout << "Enter a number: ";
int x{};
std::cin >> x;
if (x < 10)
std::cout << x << "is less than 10\n";
else if (x <= 20) // x должен быть не менее 10
std::cout << x << "is between 10 and 20\n";
else
std::cout << x << "is greater than 20\n";
return 0;
}
Вот еще один пример, в котором для проверки нескольких условий в одном операторе if
используются логические операторы:
#include <iostream>
int main()
{
std::cout << "Enter an integer: ";
int x{};
std::cin >> x;
std::cout << "Enter another integer: ";
int y{};
std::cin >> y;
if (x > 0 && y > 0) // && - логическое И - проверяет, истинны ли оба условия
std::cout << "both numbers are positive\n";
else if (x > 0 || y > 0) // || - логическое ИЛИ - проверяет, истинно ли какое-либо из условий
std::cout << "One of the numbers is positive\n";
else
std::cout << "Neither number is positive\n";
return 0;
}
Пустые инструкции
Пустая инструкция – это инструкция, состоящая только из точки с запятой:
if (x > 10)
; // это пустая инструкция
Пустые инструкции ничего не делают. Обычно они используются, когда язык требует существования инструкции, но программисту она не нужна. Для удобства чтения пустые инструкции обычно помещаются в отдельные строки.
Мы увидим примеры намеренных пустых инструкций позже в этой главе, когда будем рассматривать циклы. Пустые инструкции редко используются с операторами if
намеренно. Однако они могут непреднамеренно вызвать проблемы у начинающих (или неосторожных) программистов. Рассмотрим следующий фрагмент:
if (nuclearCodesActivated());
blowUpTheWorld();
В приведенном выше фрагменте кода программист случайно поставил точку с запятой в конце оператора if
(распространенная ошибка, поскольку точкой с запятой заканчиваются многие инструкции). Эта незаметная ошибка нормально компилируется и вызывает выполнение фрагмента, как если бы он был написан следующим образом:
if (nuclearCodesActivated())
; // точка с запятой действует как пустая инструкция
blowUpTheWorld(); // и эта строка выполняется всегда!
Предупреждение
Будьте осторожны, чтобы не «завершить» ваш оператор if
точкой с запятой, иначе ваши условные инструкции будут выполняться постоянно (даже если они находятся внутри блока).
operator==
и operator=
внутри условного выражения
Внутри вашего условного выражения при проверке равенства вы должны использовать operator==
, а не operator=
(который является присваиванием). Рассмотрим следующую программу:
#include <iostream>
int main()
{
std::cout << "Enter 0 or 1: ";
int x{};
std::cin >> x;
if (x = 0) // упс, здесь мы использовали присваивание вместо проверки на равенство
std::cout << "You entered 0";
else
std::cout << "You entered 1";
return 0;
}
Эта программа будет скомпилирована и запущена, но в некоторых случаях даст неверный результат:
Enter 0 or 1: 0
You entered 1
Фактически, эта программа всегда будет выдавать результат «You entered 1». Это происходит потому, что x = 0
сначала присваивает значение 0 переменной x
, а затем вычисляет значение x
, которое теперь равно 0, что является логическим значением false
. Поскольку условие всегда ложно, всегда выполняется инструкция else
.