Условия и циклы

Зачем нужны условия и циклы

Программа — это не только вычисления, но и решения: «если число чётное — скажи об этом», «повторяй, пока не закончатся элементы». Условия (if, switch) выбирают путь, циклы (for, while, do…while) повторяют действия.

Аналогия. Условие — развилка на дороге: поедешь налево или направо, зависит от знака. Цикл — конвейер: одну и ту же операцию повторяют, пока на ленте есть детали.

Логика: сравнения и логические операторы

ОператорЗначение
==равно
!=не равно
<, >, <=, >=сравнения
&&логическое «и»
||логическое «или»
!логическое «не»

Истина и ложь в C: почему мы почти не используем bool

Условие в C — это любое выражение: ноль — «ложь», любое ненулевое значение — «истина». Поэтому if (x) — то же самое, что if (x != 0), а if (!x) — то же, что if (x == 0).

Отдельного типа для «да/нет» в языке долго не было:

  • C23 (наш основной стандарт) ввёл bool, true и false как ключевые слова языка: подключать <stdbool.h> не нужно (заголовок остался и не мешает).
  • C99–C17: тип _Bool уже существовал, но имени bool в языке не было — <stdbool.h> определял макросы bool → _Bool, true → 1, false → 0. Забыли заголовок — код не собрался.
  • C89/C90 и часть старых компиляторов: не было и этого; для «да/нет» использовали int со значениями 0 и 1.

В примерах курса мы пишем int и явные сравнения (0/1), и вот почему:

  1. Переносимость. Код с int собирается в любом стандарте и на любом компиляторе; bool требует как минимум C99, а как ключевое слово — только C23.
  2. С библиотечными функциями bool теряет смысл. Они возвращают int, и смысл там тоньше, чем «да/нет»:
    • strcmp(a, b) возвращает 0 (строки равны), отрицательное или положительное число (порядок). bool eq = strcmp(a, b); даст true при не равных строках. Правильно: if (strcmp(a, b) == 0).
    • fscanf(...) возвращает число прочитанных полей или EOF (−1). bool ok = fscanf(...) превратит и 1, и EOF в true. Правильно: if (fscanf(f, "%d", &x) == 1).
  3. Приведение к bool скрывает ошибки. Любое значение приводится к 0/1 по правилу x != 0: bool b = 2; даст 1, а в выражениях bool снова повышается до int, поэтому if (b + 1) — это уже совсем про другое.
  4. Явное сравнение читается лучше. if (x != 0), if (count), if (strcmp(a, b) == 0) — сразу видно, что именно проверяется, и «0 = успех» не путается с «0 = ложь».

Когда bool всё-таки уместен: если компилятор точно поддерживает C23 (или включён C99+ и подключён <stdbool.h>) и в команде так договорились — для «чистых» флагов вида bool found = false;. В лекционном коде и в решениях лабораторных мы этого не делаем: так код совместим со всеми версиями C, которые мы разбираем, и лучше видно, как язык устроен внутри.

Ветвление: if / else if / else

#include <stdio.h>

int main(void)
{
    int mark;
    if (scanf("%d", &mark) != 1)
    {
        printf("Ошибка ввода\n");
        return 1;
    }

    if (mark >= 5)
    {
        printf("отлично\n");
    }
    else if (mark == 4)
    {
        printf("хорошо\n");
    }
    else if (mark == 3)
    {
        printf("удовлетворительно\n");
    }
    else
    {
        printf("надо пересдать\n");
    }
    return 0;
}

Проверяются условия сверху вниз, выполняется первая подходящая ветка. (Здесь же видно и практическое применение возвращаемого значения scanf: он вернул 0, если число не прочитано, и мы отреагировали на ошибку ввода.)

Тернарный оператор

Короткая форма «если — то — иначе» для выражений:

int a = 7, b = 4;
int max = (a > b) ? a : b;     /* max = 7 */
printf("max = %d\n", max);

switch — выбор по значению

int day = 3;
switch (day)
{
    case 1: printf("понедельник\n"); break;
    case 2: printf("вторник\n"); break;
    default: printf("другой день\n"); break;
}

break обязателен: без него выполнение «проваливается» в следующий case.

Циклы

for — когда число повторений известно

int sum = 0;
for (int i = 1; i <= 10; i++)   /* i: 1, 2, …, 10 */
{
    sum += i;
}
printf("sum = %d\n", sum);      /* 55 */

Три части цикла: инициализация (int i = 1), условие (i <= 10), шаг (i++). Любую из них можно опустить, но точку с запятой — нельзя.

while — когда важно только условие

int n, digits = 0;
scanf("%d", &n);
while (n > 0)                   /* считаем цифры числа */
{
    digits++;
    n /= 10;
}
printf("цифр: %d\n", digits);

do … while — тело выполнится хотя бы раз

int value;
do
{
    printf("Введите положительное число: ");
    scanf("%d", &value);
} while (value <= 0);           /* повторяем, пока ввод плохой */

break и continue

  • break — немедленно выйти из цикла;
  • continue — пропустить остаток тела и перейти к следующей итерации.
for (int i = 1; i <= 10; i++)
{
    if (i % 2 == 0) continue;   /* пропускаем чётные */
    if (i > 7) break;           /* дальше не идём */
    printf("%d ", i);           /* 1 3 5 7 */
}

Вложенные циклы

for (int i = 1; i <= 3; i++)        /* строки */
{
    for (int j = 1; j <= 3; j++)    /* столбцы */
    {
        printf("%4d", i * j);       /* таблица умножения 3×3 */
    }
    printf("\n");
}

Полезные шаблоны

/* накопление суммы и произведения на отрезке 1..n */
int sum = 0, product = 1;
for (int i = 1; i <= n; i++)
{
    sum += i;
    product *= i;
}

/* факториал */
long long fact = 1;
for (int i = 2; i <= n; i++) fact *= i;

/* сумма цифр числа */
int digitsSum = 0;
int copy = n;
while (copy > 0)
{
    digitsSum += copy % 10;
    copy /= 10;
}

/* переворот числа */
long long reversed = 0;
copy = n;
while (copy > 0)
{
    reversed = reversed * 10 + copy % 10;
    copy /= 10;
}

/* проверка простого числа */
int isPrime = 1;
if (n < 2) isPrime = 0;
for (int d = 2; d * d <= n && isPrime; d++)
{
    if (n % d == 0) isPrime = 0;
}

Частые ошибки

ОшибкаЧто происходит
if (x = 5) вместо if (x == 5)присваивание вместо сравнения — условие всегда истинно
if (x > 0); — лишняя ;тело условия пустое, следующий блок выполняется всегда
i <= n вместо i < nлишняя итерация; а при обходе массивов это будет выход за границы
забыли break в switch«проваливание» в следующий case
бесконечный цикл while (i < n) без i++программа зависает
int avg = sum / n;целое деление вместо дробного

Что запомнить

  1. Ноль — «ложь», всё ненулевое — «истина»; if (x) равнозначно if (x != 0).
  2. В C23 есть bool/true/false, но в курсе мы используем int и явные сравнения — из-за переносимости и потому, что библиотеки возвращают int с более богатым смыслом.
  3. Условия читаются сверху вниз — порядок ветвей важен.
  4. = и == — разные вещи; компилятор об этой опечатке часто молчит.
  5. break выходит из цикла, continue — только из текущей итерации.

Подтверждение

—