+---------------------------+
|.-------------------------.|
|| kee_reel@blog:~/ru $ cd ||
|| ссылки контакты         ||
|| c c++ linux opengl sql  ||
|| python сети             ||
||                         ||
|.-------------------------.|
+-::---------------------::-+
.---------------------------.
 // /oooooooooooooooooooooo\\ \\ 
 // /oooooooooooooooooooooooo\\ \\ 
//-------------------------------\\
\\-------------------------------//


C. Циклы

Время чтения: 5 минут

Цикл (loop) – это механизм, позволяющий выполнить множество схожих действий.

В языке си есть три вида циклов:

  • for
  • while
  • do while

Цикл for

Вот пример определения цикла для решения задачи “выводим все чётные числа от 1 до n (не включительно)”:

int n;
scanf("%d", &n);
for(int i = 1; i < n; i++)
{
    if(i % 2 == 0)
    {
        printf("%d\n", i);
    }
}

Давай разберём из чего состоит цикл.

Создание временной переменной

int i = 1;

Этот код выполняется перед стартом цикла – обычно в нём создаётся временная переменная, которая будет использована внутри цикла.

Условие завершения

i < n;

Этот код исполняется перед каждым исполнением тела цикла, и возвращает булевое значение.

В этом примере временная переменная i сравнивается с введённым числом n.

Допустим, мы ввели число 10 – условие будет верно до тех пор, пока i меньше 10. В момент, когда выражение в условии вернёт значение ложь – цикл закончится.

Инкремент временной переменной

i++

Здесь обычно пишется инкремент временной переменной. Благодаря этому коду цикл изменяет значение i после прохода каждого цикла.

Тело цикла

{
    if(i % 2 == 0)
    {
        printf("%d\n", i);
    }
}

Обычно, здесь пишется код, который как-то использует временные переменные – здесь это переменная i, которая используется для проверки на делимость.

Всё вместе

Ещё раз напишу код с циклом и добавлю дополнительные логи (вывод сообщений), чтобы лучше понять что происходит:

int n;
scanf("%d", &n);
for(int i = 1; i < n; i++)
{
    if(i % 2 == 0)
    {
        printf("%d\n", i);
    }
}

Таким образом, последовательность применения выражений внутри определения цикла такова:

  1. Создание временной переменной:
int i = 1;

Переходим к шагу 2.

  1. Проверка условия:
i < n;

Если значение равно:

  • истина – переходим к шагу 3
  • ложь – переходим к шагу 5.
  1. Исполнение тела цикла:
{
    if(i % 2 == 0)
    {
        printf("%d\n", i);
    }
}

Переходим к шагу 4.

  1. Инкремент временной переменной:
i++

Переходим к шагу 2.

  1. Конец цикла.

Вот что выведет эта программа (если введём 10 с клавиатуры):

2
4
6
8

Видишь, строка “Проход цикла:” заканчивается на 8-ке, потому что когда i становится 10, условие i < n не выполняется, и цикл завершается.

Цикл while

Если ты разобрался с циклом for, то с while у тебя не возникнет проблем – он намного легче.

Вот как он выглядит (я описал с его помощью ту же задачу, что и выше):

int x;
scanf("%d", &x);
printf("Поиск делителей числа %d", x);
int div = 2;
while(div < x)
{
    printf("Проход цикла: %d\n", div);
    if(x % div == 0)
    {
        printf("Нашли делитель: %d\n", div);
    }
    div++;
}

Здесь нужно описать только условие, которое будет проверяться перед проходом каждого цикла. Создание временной переменной и инкремент я просто перенёс в соответствующие места.

Вывод программы будет таким же.

Очень важно не забывать про инкремент при использовании цикла while! Я уже и не упомню сколько раз у меня получался бесконечный цикл (условие цикла всегда истинно), из-за отсутствия инкремента переменной – она оставалась на начальном значении, и цикл бесконечно молотил впустую.

Цикл do while

Те же яйца, только сбоку – в цикле while проверка условия производится перед исполнением тела цикла, а в цикле do while – проверка производится после тела цикла.

int x;
scanf("%d", &x);
printf("Поиск делителей числа %d", x);
int div = 2;

if(div >= x)
{
    return;
}

do
{
    printf("Проход цикла: %d\n", div);
    if(x % div == 0)
    {
        printf("Нашли делитель: %d\n", div);
    }
    div++;
}
while(div < x);

Вроде бы ничего особо не изменилось, но я зачем-то добавил проверку:

if(div >= x)
{
    return;
}

Объясняю зачем – из-за того, что условие цикла проверится только после прохождения тела цикла, мне надо убедиться что введённое пользователем число подходит под условие div < x.

Представь что этого дополнительного условия нет – если пользователь введёт -10:

  • Тело цикла начнёт выполняться
  • Мы напишем “Проход цикла: 2”
  • Мы найдём делитель -10 при dev == 2, и напишем “Нашли делитель: 2”
  • И только после этого дойдём до проверки div < x (-10 < 2), которая вернёт ложь

А это уже баг!

Баг (от агл. bug – жук) – это жаргонный термин в программировании, обозначающее ошибку при написании программы, которая привела к нежелательному поведению. Есть версия, что впервые в отношении программной ошибки это термин применили в 1947 году, когда нашли вполне реального “жука” в реле вычислительной машины: Первый баг

Прерывание цикла и пропуск итерации

В языке Си (а также в C++, Python, JS, Go и прочих языках) есть способ прервать выполнение цикла и пропустить текущую итерацию.

break

break позволяет выйти из цикла раньше времени.

Например, у нас задача “вывести 10 чётных чисел от 1 до 100”:

int count = 10;
for(int i = 1; i < 100; i++)
{
    if(i % 2 == 0)
    {
        count--;
        printf("%d\n", i);
        if(count == 0)
            break;
    }
}

continue

continue позволяет пропустить текущую итерацию, и сразу начать следующую (произойдёт инкремент временной переменной).

Задача “на заданном промежутке посчитать сумму нечётных чисел и вычесть из неё сумму чисел делящихся на 3”:

Та же задача “вывести 10 чётных чисел от 1 до 100”:

int count = 10;
for(int i = 1; i < 100; i++)
{
    if(i % 2 != 0)
        continue;

    count--;
    printf("%d\n", i);
    if(count == 0)
        break;
}

Обычно continue используется для сокращения уровня вложенности условий в цикле.

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

Внутри одного цикла, ты можешь запускать ещё циклы. Задача “вывести N простых чисел”:

int N;
scanf("%d", &N);
// Пока N больше нуля
for(int i = 2; N > 0; i++)
{
    printf("%d\n", i);
    char is_prime_number = 1;
    // Для всех j от 2 до i
    for(int j = 2; j < i; j++)
    {
        // Простое число может делиться только на 1 и на само себя.
        // Если делится на что-то ещё, то оно не простое.
        if(i % j == 0)
        {
            is_prime_number = 0;
            break;
        }
    }
    if(is_prime_number)
    {
        printf("%d\n", i);
        N--;
    }
}

То есть мы для каждого числа i создаём цикл, в котором пытаемся его поделить на все числа от 2 до i.

Задание на закрепление

Для всех числел на промежутке [2, N] (N вводится с клавиатуры), найти сумму квадратов чётных чисел и разделить её на их количество.

Заключение

Итого, ты изучил:

  • Цикл for
  • Цикл while
  • Цикл do while
  • Прерывание цикла (break)
  • Пропуск итерации (continue)
  • Вложенные циклы

С помощью циклов можно уже решать довольно сложные задачи – обходить большие массивы данных (про это будет потом), вычислять приближённые значения математических функций, искать числа с определёнными свойствами (например найти 1000 простых числел) и мнооогое другое.

Если что – пиши, я помогу и постараюсь объяснить лучше.

Дальше мы рассмотрим то, как можно структурировать твои программы – функции.


▲ В начало ▲