Условия и циклы
Зачем нужны условия и циклы
Программа — это не только вычисления, но и решения: «если число чётное — скажи об этом»,
«повторяй, пока не закончатся элементы». Условия (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), и вот почему:
- Переносимость. Код с
intсобирается в любом стандарте и на любом компиляторе;boolтребует как минимум C99, а как ключевое слово — только C23. - С библиотечными функциями
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).
- Приведение к
boolскрывает ошибки. Любое значение приводится к0/1по правилуx != 0:bool b = 2;даст1, а в выраженияхboolснова повышается доint, поэтомуif (b + 1)— это уже совсем про другое. - Явное сравнение читается лучше.
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; | целое деление вместо дробного |
Что запомнить
- Ноль — «ложь», всё ненулевое — «истина»;
if (x)равнозначноif (x != 0). - В C23 есть
bool/true/false, но в курсе мы используемintи явные сравнения — из-за переносимости и потому, что библиотеки возвращаютintс более богатым смыслом. - Условия читаются сверху вниз — порядок ветвей важен.
=и==— разные вещи; компилятор об этой опечатке часто молчит.breakвыходит из цикла,continue— только из текущей итерации.