Условный оператор if. Конструкция if-else

Практический курс по C/C++: https://stepik.org/course/193691

На этом занятии мы познакомимся с условным оператором if. Что он делает и для чего нужен? На предыдущем занятии мы с вами рассматривали операции сравнения, которые выдают либо 0 (false), либо 1 (true). Так вот, чтобы программа могла менять свое поведение в зависимости от этих значений, как раз можно воспользоваться оператором if.

Принцип его работы очень прост. Процессор последовательно выполняет операторы (команды) и, как только доходит до условного оператора, то при истинности условия выполняется одна группа операторов, а иначе – другая:

Это похоже на процесс ветвления при выполнении команд. Поэтому условные операторы еще иногда называют операторами ветвления.

Чтобы мы могли использовать оператор if в своих программах, нам нужно знать его синтаксис. В самых простых вариациях он имеет следующий вид:

if(<выражение>) оператор;

или

if(<выражение>) {
    оператор_1;
    ...
    оператор_N;
}

Здесь выражение – это любая конструкция языка Си, которая возвращает числовые значения. Число 0 будет интерпретировано как false, а любое не нулевое – как true. После круглых скобок должен идти оператор, который выполняется при истинности выражения. Если же нужно выполнить группу операторов, то для этого используются операторные фигурные скобки, которые можно воспринимать, как единый составной оператор. Поэтому, формально, после оператора if всегда следует один оператор.

Давайте для примера посмотрим, как можно использовать этот оператор для вычисления модуля числа:

#include <stdio.h>
 
int main(void)
{
    int x;
    scanf("%d", &x);
 
    if(x < 0) x = -x;
    printf("x = %d\n", x);
 
    return 0;
}

Вначале в переменную x вводится с клавиатуры некоторое целое значение, а затем, делается проверка: если число x меньше нуля (то есть, отрицательное), то выполняется команда x = -x и знак меняется на противоположный, число становится положительным. Причем, эта команда будет выполнена только при условии, что x меньше нуля. Если же x больше или равен нулю, то команда пропускается и число остается положительным. В результате мы этой проверкой формируем модуль числа.

Обратите внимание, что при истинности условия выполняется только один оператор x = -x. Функция printf(), которая записана следующей строчкой, находится вне условного оператора и выполняется всегда. С условным оператором if связана только одна команда x = -x.

Кстати, в этой же программе было бы правильно проверить корректность введенного значения. Для этого достаточно проверить, что функция scanf() вернула единицу. Но я пропишу обратное условие:

    if(scanf("%d", &x) != 1) {
        printf("Error input");
        return 0;
    }

Смотрите, в качестве выражения здесь выступает вызов функции scanf() с последующей проверкой на неравенство единице. Так тоже вполне можно делать. Затем, если вводится некорректное значение, то это условие оказывается истинным и выполняются операторы внутри фигурных скобок. Будет выведена строка с сообщением об ошибке и оператор return 0, который завершит функцию main() и, как следствие, всю программу.

Не редко в программах на языке Си можно встретить использование оператора if с одной переменной в качестве условия, например, так:

if(x) x = -x;

Это тоже допустимо, т.к. в круглых скобках можно указывать любое выражение, которое возвращает числовое значение. И легко догадаться, если значение x равно 0, то условие считается ложным, а при любом другом – истинным.

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

if(!x) x = -x;

Тогда все будет наоборот, условие истинно при x равном 0, и ложно при любом другом числовом значении.

Вообще, используя логические операции, можно формировать произвольные составные условия. Например, проверить попадание точки с координатой x в диапазон [3; 11]:

    if(x >= 3 && x <= 11) 
        printf("x in [3; 11]\n");

А в качестве самостоятельного задания напишите проверку непопадания значения x в этот же диапазон [3; 11]. Вся информация для этого у вас уже есть.

Конструкция if-else

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

#include <stdio.h>
 
int main(void)
{
    int x;
    if(scanf("%d", &x) != 1) {
        printf("Error input");
        return 0;
    }
 
    if(x < 0) printf("x < 0\n");
    if(x >= 0) printf("x >= 0\n");
        
    return 0;
}

У нас здесь два подряд идущих условия. Причем, взаимоисключающие. Действительно, x не может быть одновременно и меньше нуля и больше либо равно. После ввода значения возможен только один вариант. В таких ситуациях было бы логично проверить первое условие, если оно истинно, выполнить соответствующий оператор, а иначе, выполнить другой оператор без какой-либо проверки. Такую конструкцию в языке Си можно записать следующим образом:

if(<выражение>) оператор_1;
else оператор_2;

или с использованием операторных скобок:

if(<выражение>) {
    оператор_1;
    ...
    оператор_N;
}
else {
    оператор_1;
    ...
    оператор_M;
}

Здесь ключевое слово else как раз и соответствует ветке «иначе», то есть, оператор_2 (или операторы от 1 до M в фигурных скобках) выполняется, если не сработало (оказалось ложным) выражение в условном операторе if. Таким образом, если условие в операторе if истинно, то выполняется оператор_1 (или аналогичные операторы в фигурных скобках), а иначе – оператор_2 (или аналогичные операторы в фигурных скобках).

Давайте перепишем нашу программу с использованием этой конструкции. Получим:

#include <stdio.h>
 
int main(void)
{
    int x;
    if(scanf("%d", &x) != 1) {
        printf("Error input");
        return 0;
    }
 
    if(x < 0) 
        printf("x < 0\n");
    else 
        printf("x >= 0\n");
        
    return 0;
}

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

    if(x < 0) 
        printf("x < 0\n");
    else if(x > 0) 
            printf("x > 0\n");
        else
            printf("x == 0\n");

Здесь после первого else записан еще один условный оператор if с проверкой x > 0 и у этого второго условного оператора также имеется свой блок else. Получилась такая вложенная конструкция из операторов if-else.

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

    if(x < 0) 
        printf("x < 0\n");
    else {
        if(x > 0) 
            printf("x > 0\n");
        else
            printf("x == 0\n");
    }

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

    if(x < 0) 
        printf("x < 0\n");
    else if(x > 0) 
        printf("x > 0\n");
    else
        printf("x == 0\n");

Читается и понимается такая конструкция достаточно просто. Сначала идет проверка первого условия x < 0. Если оно истинно, то выполняется оператор по условию и дальнейшие блоки пропускаются. Если же первое условие ложно, то попадаем в блок else первого оператора if и там осуществляется проверка вложенного условия x > 0. Если это условие истинно, то выполняется оператор printf("x > 0\n"), а если ложно, то переходим в последний блок else, который гарантированно срабатывает с выполнением оператора printf("x == 0\n").

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

    if(x < 10) 
        printf("x in [0; 9]\n");
    else if(x < 100) 
        printf("x in [10; 99]\n");
    else if(x < 1000)
        printf("x in [100; 999]\n");
    else
        printf("x > 999\n");

Формально условия x < 10; x < 100; x < 1000 не взаимоисключающие, но порядок их следования таков, что они позволяют выделять нам нужные независимые группы чисел. То есть, творческое мышление, при использовании различных операторов, никто не отменял. Иначе бы программы уже давно писались бы не людьми, а теми же компьютерами. Хотя такие попытки предпринимаются, в частности, с помощью нейронных сетей. Но пока до человеческого уровня им далеко и в ближайшие 10-20 лет грамотный программист в этой области незаменим.

Практический курс по C/C++: https://stepik.org/course/193691

Видео по теме