На этом занятии начнем знакомиться с еще одной ключевой конструкцией – циклами.
Вначале, давайте я на простом примере покажу, о чем идет речь. Представим, что пользователь
вводит с клавиатуры целое положительное значение n и нам нужно
вычислить следующую сумму:
1^2 + 2^2 + 3^2
+ 4^2 + ... + n^2
Понятно, что мы
не можем заранее прописать всю эту сумму, так как значение n попросту
неизвестно на момент написания программы. Переменная n принимает
конкретное значение только в процессе выполнения программы. Поэтому реализовать
такую конструкцию можно только с помощью операторов циклов.
В общем случае
циклы позволяют реализовывать некие повторяющиеся действия. Например,
предположим, что маленькие панды прыгают с горки в течение часа, пока
мама-панда не позовет всех к столу – кушать. Формализовать это можно, например,
так:
цикл
(пока не прошел час):
прыгаем с
горки
То есть, пока
истинно условие, цикл работает, как только условие становится ложным (прошел
час) цикл завершается. Ровно так работает цикл while, о котором и
пойдет речь на этом занятии. Он имеет, следующее определение (синтаксис):
while(<условие>)
оператор;
или
while(<условие>) {
оператор 1;
...
оператор N;
}
В
программировании оператор или блок операторов, выполняющихся в цикле, называют телом
цикла. А один проход выполнения оператора цикла – итерацией. Сам
цикл while относится к циклам
с предусловием, т.к. сначала проверяется условие цикла и только после этого
(при истинности условия) выполняется текущая итерация.
Давайте вернемся
к исходной задаче – вычисления суммы квадратов целых чисел от 1 до n и посмотрим,
как здесь можно использовать цикл while:
#include <stdio.h>
int main(void)
{
int n;
int s = 0;
if(scanf("%d", &n) != 1) {
printf("Error input\n");
return 0;
}
while(n > 0) {
s += n*n;
n--;
}
printf("s = %d\n", s);
return 0;
}
Вначале мы
объявили две переменные n и s, причем, в s будет храниться
сумма и она принимает начальное значение 0. Затем, запрашивается ввод в
переменную n и если он
некорректен, то программа завершается. Иначе запускается цикл while. В круглых
скобках прописано условие продолжения цикла: пока n больше нуля. А
в теле цикла определены два оператора: сначала к переменной s прибавляем
квадрат наибольшего значения n, а потом, уменьшаем переменную n на единицу. В
результате у нас будет образовываться следующая сумма:
s = n^2 + (n-1)^2 + … + 1^2
Как только
значение n становится
равным 0, условие цикла становится ложным и оператор while прекращает свою
работу. Управление переходит к следующему оператору printf().
Давайте теперь
несколько изменим нашу программу и запишем цикл while следующим
образом:
Будет ли это тем
же самым или программа станет работать по другому? Проверим это. Запустим и
введем число 4. Получим сумму:
s = 14
Очевидно это не
вся прежняя сумма:
s = 1^2 + 2^2 +
3^2 + 4^2 = 30
У нас не хватает
последнего слагаемого 4^2 = 16. Почему так произошло? Все просто. Сначала
выполняется сравнение переменной n с нулем (4 > 0) и после этого
операция декремента. То есть, когда выполнение программы переходит к оператору
«s += n*n;» тела цикла переменная n уже на единицу меньше. Возможно,
некоторых из вас это удивит, так как операция декремента записана в постфиксной
форме (после имени переменной), а значит, она должна выполняться в последнюю
очередь. Но логика работы здесь несколько иная. Любое выражение, которое
прописывается в условии, сначала полностью вычисляется и только затем
осуществляется переход к телу цикла. И это всегда так. Этот важный момент нужно
запомнить и знать. Именно поэтому переменная n гарантированно
будет уменьшена на единицу при переходе к оператору «s += n*n;».
Кстати,
последнее слагаемое в нашем примере будет 0^2 = 0. Поэтому (если ноль нам не
нужен) правильнее было бы прописать эту условие так:
Префиксная форма
записи операции декремента сначала уменьшит значение n на единицу и
только после этого будет осуществляться сравнение с нулем.
Давайте теперь
немного усложним программу и сделаем ограничение на максимальное значение n на тот случай,
если пользователь введет слишком большое число. Например, сделаем так, чтобы слагаемых
было не больше 10:
int i = 0;
while(++i <= n && i <= 10)
s += i*i;
Условие в цикле while пришлось
переписать с использованием дополнительной вспомогательной переменной i, которая, по
сути, является счетчиком итераций. Ее первое значение при подсчете суммы будет
равно 1, затем, 2 и так пока либо не дойдет до n, либо до 10.
Давайте
внимательнее посмотрим на условие. Вначале записана операция инкремента в
префиксной форме. Это означает, что значение переменной i увеличится на
единицу и только потом выполнится операция сравнения. Мало того, здесь мы можем
точно гарантировать, что операция инкремента (в любой форме записи: префиксной
или постфиксной) отработает до перехода к следующей проверке i <= 10. Так
заложено в стандарте языка Си. И это правило справедливо для всех логических
связок:
&& и ||
где бы они ни
использовались: в операторах циклов или условных операторах.
Благодаря этому
мы можем четко понимать, как отработает данный цикл. Вначале обязательно
увеличится значение переменной i на единицу, затем, проверится условие i <= n и только после
этого (при необходимости) будет проверяться второе подусловие i <= 10.
Вообще, в
качестве условия цикла while можно записывать любое выражение.
Например, мы хотим считывать целые числа с клавиатуры, пока пользователь не
введет 0. Сделать это можно следующим образом:
#include <stdio.h>
int main(void)
{
int s = 0;
int x = 1;
while(scanf("%d", &x) == 1 && x != 0)
s += x;
printf("s = %d\n", s);
return 0;
}
Здесь цикл while будет работать
до тех пор, пока пользователь либо вводит корректные данные (целые числа), либо
введет число 0. В самом цикле выполняется суммирование введенных числовых
значений, а после цикла отображение полученного результата с помощью функции printf().
Или можно
немного усложнить пример и выполнять суммирование только четных чисел (или
только положительных). Для этого достаточно внутри цикла while указать нужное
нам условие, например, следующим образом:
while(scanf("%d", &x) == 1 && x != 0)
if(x % 2 == 0)
s += x;
То есть, в теле
цикла while можно
прописывать абсолютно любые операторы языка Си. И, обратите внимание, в данном
случае мы не указывали фигурные скобки, т.к. в теле цикла формально записан
один оператор if, а для одного
оператора фигурные скобки не обязательны. Хотя, при желании их можно записать:
while(scanf("%d", &x) == 1 && x != 0) {
if(x % 2 == 0)
s += x;
}
Программа от
этого не изменится.
В заключение
занятия отмечу один важный момент использования операторных фигурных скобок. Обычная
переменная, объявленная внутри них, например:
while(scanf("%d", &x) == 1 && x != 0) {
int res = x % 2;
if(res == 0)
s += x;
}
существует
только в пределах этих операторных скобок и перестает существовать, когда
выполнение программы перейдет к следующему оператору за пределами этих фигурных
скобок. То есть, если мы захотим вывести значение переменной res после цикла while:
то при
компиляции возникнет ошибка, что переменная res не определена.
На самом деле,
это очень полезное поведение, заложенное в язык Си. На практике часто внутри
фигурных скобок объявляют временные переменные, необходимые для отработки
внутренней логики. Потом они просто автоматически исчезают и не мешают работе
остальной программы. Обо всем этом мы еще подробнее будем с вами говорить,
когда речь пойдет о локальных и глобальных переменных.
На этом завершим
первое занятие по циклам. Из него вам должно быть понятно для чего вообще нужны
циклы и как работает оператор цикла while.