7.3 – Распространенные проблемы при работе с операторами if

Добавлено 23 мая 2021 в 23:45

Данный урок является продолжением урока «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.

Теги

C++ / CppLearnCppДля начинающихОбучениеПрограммированиеУсловный оператор

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.