На этом уроке мы детально рассмотрим цикл while, его конструкцию, особенности и использование.
Цикл while
Цикл while является самым простым из четырех циклов, которые есть в языке C++. Он очень похож на ветвление if/else:
while (условие)
тело цикла;
Цикл while объявляется с использованием ключевого слова while. В начале цикла обрабатывается условие
. Если его значением является true (любое ненулевое значение), то тогда выполняется тело цикла
.
Однако, в отличие от оператора if, после завершения выполнения тела цикла
, управление возвращается обратно к while и процесс проверки условия повторяется. Если условие опять является true, то тогда тело цикла
выполняется еще раз.
Например, следующая программа выводит все числа от 0 до 9:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> int main() { int count = 0; while (count < 10) { std::cout << count << " "; ++count; } std::cout << "done!"; return 0; } |
Результат выполнения программы:
0 1 2 3 4 5 6 7 8 9 done!
Рассмотрим детально эту программу. Во-первых, инициализируется переменная: int count = 0;
. Условие 0 < 10
имеет значение true, поэтому выполняется тело цикла. В первом стейтменте мы выводим 0
, а во втором — выполняем инкремент переменной count
. Затем управление возвращается к началу цикла while для повторной проверки условия. Условие 1 < 10
имеет значение true, поэтому тело цикла выполняется еще раз. Тело цикла будет повторно выполняться до тех пор, пока переменная count
не будет равна 10
, только в том случае, когда результат условия 10 < 10
будет false, цикл завершится.
Тело цикла while может и вообще не выполняться, например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
#include <iostream> int main() { int count = 15; while (count < 10) { std::cout << count << " "; ++count; } std::cout << "done!"; return 0; } |
Условие 15 < 10
сразу принимает значение false, и тело цикла пропускается. Единственное, что выведет эта программа:
Бесконечные циклы
С другой стороны, если условие цикла всегда принимает значение true, то и сам цикл будет выполняться бесконечно. Это называется бесконечным циклом. Например:
1 2 3 4 5 6 7 8 9 10 |
#include <iostream> int main() { int count = 0; while (count < 10) // это условие никогда не будет false std::cout << count << " "; // поэтому эта строка будет выполняться постоянно return 0; // а эта строка никогда не выполнится } |
Поскольку переменная count
не увеличивается на единицу в этой программе, то условие count < 10
всегда будет true. Следовательно, цикл никогда не будет завершен, и программа будет постоянно выводить 0 0 0 0 0...
.
Мы можем преднамеренно объявить бесконечный цикл следующим образом:
1 2 3 4 |
while (1) // или while (true) { // Этот цикл будет выполняться бесконечно } |
Единственный способ выйти из бесконечного цикла — использовать операторы return, break, goto, выбросить исключение или воспользоваться функцией exit().
Программы, которые работают до тех пор, пока пользователь не решит остановить их, иногда преднамеренно используют бесконечные циклы вместе с операторами return, break или функцией exit() для завершения цикла. Распространена такая практика в серверных веб-приложениях, которые работают непрерывно и постоянно обслуживают веб-запросы.
Счетчик цикла while
Часто нам нужно будет, чтобы цикл выполнялся определенное количество раз. Для этого обычно используется переменная в виде счетчика цикла. Счетчик цикла — это целочисленная переменная, которая объявляется с единственной целью: считать, сколько раз выполнился цикл. В вышеприведенных примерах переменная count
является счетчиком цикла.
Счетчикам цикла часто дают простые имена, такие как i
, j
или k
. Однако в этих именах есть одна серьезная проблема. Если вы захотите узнать, где в вашей программе используется счетчик цикла и воспользуетесь функцией поиска символов i
, j
или k
, то в результате получите половину своей программы, так как i
, j
или k
используются во многих именах. Следовательно, лучше использовать iii
, jjj
или kkk
в качестве имен для счетчиков. Они более уникальны, их значительно проще найти, и они выделяются в коде. А еще лучше использовать «реальные» имена для переменных, например, count
или любое другое имя, которое предоставляет контекст использования этой переменной.
Также для счетчиков цикла лучше использовать тип signed int. Использование unsigned int может привести к неожиданным результатам. Например:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <iostream> int main() { unsigned int count = 10; // Считаем от 10 к 0 while (count >= 0) { if (count == 0) std::cout << "blastoff!"; else std::cout << count << " "; --count; } return 0; } |
Взгляните на эту программу еще раз и постарайтесь найти ошибку.
Оказывается, эта программа представляет собой бесконечный цикл. Она начинается с вывода 10 9 8 7 6 5 4 3 2 1 blastoff!
, как и предполагалось, но затем «сходит с рельсов» и начинает отсчет с 4294967295
. Почему? Потому что условие цикла count >= 0
никогда не будет ложным! Когда count = 0
, то и условие 0 >= 0
имеет значение true, выводится blastoff
, а затем выполняется декремент переменной count
, происходит переполнение и значением переменной становится 4294967295
. И так как условие 4294967295 >= 0
является истинным, то программа продолжает свое выполнение. А поскольку счетчик цикла является типа unsigned, то он никогда не сможет быть отрицательным, а так как он никогда не сможет быть отрицательным, то цикл никогда не завершится.
Правило: Всегда используйте тип signed int для счетчиков цикла.
Итерации
Каждое выполнение цикла называется итерацией (или «повтором»).
Поскольку тело цикла обычно является блоком, и поскольку этот блок выполняется по новой с каждым повтором, то любые переменные, объявленные внутри тела цикла, создаются, а затем и уничтожаются по новой. В следующем примере переменная z
создается и уничтожается 6 раз:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
#include <iostream> int main() { int count = 1; int result = 0; // переменная result определена здесь, поскольку она нам понадобится позже (вне тела цикла) while (count <= 6) // итераций будет 6 { int z; // z создается здесь по новой с каждой итерацией std::cout << "Enter integer #" << count << ':'; std::cin >> z; result += z; // Увеличиваем значение счетчика цикла на единицу ++count; } // z уничтожается здесь по новой с каждой итерацией std::cout << "The sum of all numbers entered is: " << result; return 0; } |
Для фундаментальных типов переменных это нормально. Для нефундаментальных типов переменных (таких как структуры или классы) это может сказаться на производительности. Следовательно, нефундаментальные типы переменных лучше определять перед циклом.
Обратите внимание, переменная count
объявлена вне тела цикла. Это важно и необходимо, поскольку нам нужно, чтобы значение переменной сохранялось на протяжении всех итераций (не уничтожалось по новой с каждым повтором цикла).
Иногда нам может понадобиться выполнить что-то при достижении определенного количества итераций, например, вставить символ новой строки. Это легко осуществить, используя оператор остатка от деления со счетчиком цикла:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
#include <iostream> int main() { int count = 1; while (count <= 50) { // Выводим числа до 10 (перед каждым числом добавляем 0) if (count < 10) std::cout << "0" << count << " "; else std::cout << count << " "; // выводим остальные числа // Если счетчик цикла делится на 10 без остатка, то тогда вставляем символ новой строки if (count % 10 == 0) std::cout << "\n"; // Увеличиваем значение счетчика цикла на единицу ++count; } return 0; } |
Результат выполнения программы:
01 02 03 04 05 06 07 08 09 10
11 12 13 14 15 16 17 18 19 20
21 22 23 24 25 26 27 28 29 30
31 32 33 34 35 36 37 38 39 40
41 42 43 44 45 46 47 48 49 50
Вложенные циклы while
Также одни циклы while могут быть вложены внутри других циклов while. В следующем примере внутренний и внешний циклы имеют свои собственные счетчики. Однако, обратите внимание, условие внутреннего цикла использует счетчик внешнего цикла!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <iostream> int main() { int outer = 1; while (outer <= 5) { int inner = 1; while (inner <= outer) std::cout << inner++ << " "; // Вставляем символ новой строки в конце каждого ряда std::cout << "\n"; ++outer; } return 0; } |
Результат выполнения программы:
Тест
Задание №1
Почему в программе, приведенной выше, переменная inner
объявлена внутри блока while, а не сразу после объявления переменной outer
(вне блока while)?
Ответ №1
Переменная inner
объявлена внутри блока while так, чтобы она была восстановлена (и повторно инициализирована значением 1
) каждый раз, когда выполняется внешний цикл. Если бы переменная inner
была объявлена вне внешнего цикла while, то её значение никогда не было бы сброшено до 1
, или нам бы пришлось это сделать самостоятельно с помощью операции присваивания. Кроме того, поскольку переменная inner
используется только внутри внешнего цикла while, то имеет смысл объявить её именно там. Помните, что переменные нужно объявлять максимально близко к их первому использованию!
Задание №2
Напишите программу, которая выводит буквы английского алфавита от a
до z
вместе с кодами из ASCII-таблицы.
Подсказка: Чтобы выводить символы как целые числа — используйте оператор static_cast.
Ответ №2
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#include <iostream> int main() { char mychar = 'a'; while (mychar <= 'z') { std::cout << mychar << " " << static_cast<int>(mychar) << "\n"; ++mychar; } return 0; } |
Задание №3
Измените программу из последнего подраздела «Вложенные циклы while» так, чтобы она выводила следующее:
5 4 3 2 1
4 3 2 1
3 2 1
2 1
1
Ответ №3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
#include <iostream> int main() { int outer = 5; while (outer >= 1) { int inner = outer; while (inner >= 1) std::cout << inner-- << " "; // Вставляем символ новой строки в конце каждого ряда std::cout << "\n"; --outer; } return 0; } |
Задание №4
Теперь сделайте так, чтобы цифры выводились следующим образом (используя программу из предыдущего задания):
1
2 1
3 2 1
4 3 2 1
5 4 3 2 1
Подсказка: Разберитесь сначала, как вывести числа следующим образом:
X X X X 1
X X X 2 1
X X 3 2 1
X 4 3 2 1
5 4 3 2 1
Ответ №4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#include <iostream> int main() { // Цикл с 1 до 5 int outer = 1; while (outer <= 5) { // Числа в рядах появляются в порядке убывания, поэтому цикл начинаем с 5 и до 1 int inner = 5; while (inner >= 1) { // Первое число в любом ряде совпадает с номером этого ряда, // поэтому числа должны выводиться только если <= номера ряда (в противном случае, выводится пробел) if (inner <= outer) std::cout << inner << " "; else std::cout << " "; // вставляем дополнительные пробелы --inner; } // Этот ряд вывели, переходим к следующему std::cout << "\n"; ++outer; } } |
Мое решение 2 — й задачи.
Моё решение 4 задачи без логических ветвлений. Потратил около 40 минут на её решение
Второе задание:
Третье задание:
Четвертео задание:
Задание 4. Вроде написал так же, долго морочил голову почему получается треугольник. Так и не понял разницы
Там где «else» поставь двойной пробел — » «, а не одинарный » «.
Я написал код, который будет работать правильно даже если размер будет больше 10:
Подскажите пожалуйста.
Случайно вставил пробел в ' \n' вместо '\n' в строке (std::cout << a << " = " << static_cast<int>(a) << ' \n'; )
И получил неожиданный и не понятный результат.
Почему так происходит? Какой принцип работы оператора новой строки?
Ты используешь одинарные кавычки, а они предназначены для char'ов. Пробел тоже считается за символ.
Задание 4. Интересный получился результат, вроде бы код как в ответе, а на выходе что то другое.
OS Linux, IDE Code::Blocks
1
2 1
3 2 1
4 3 2 1
5 4 3 2 1
я думаю в строке 12 нужно добавить пробел и все получится
2 задание:
// 2 задание
4 задание
Так у тебя ж результат неверный. Сортировка не такая как нужно.
С большим удовольствием написал эти программы.
Задание №2:
main.ccp:
Задание №3:
main.ccp:
Задание 4:
main.ccp:
Благодаря этому прекрасному сайту быстро прогрессирую. Прекрасное изложение материала. Огромное спасибо Юрию!!!
4 задача
"Следовательно, лучше использовать iii, jjj или kkk в качестве имен для счетчиков."
Фу, громоздко, и не красиво.
Мой вариант ромбика, получился не большой:)
Задание №2 измененный мой вариант без кодов.
Задание №4 мой вариант.
Ромб)
Задание № 4
Задание № 3
Задание № 2
Вот более простое решение четвертого задания:
🙂 шутка
Ромб-челлендж.
Идеально ровный, почти красивый ромб.
Максимальное число (размер ромба) по краям, единица посередине.
Вот мой вариант 4 задания без использования if/else
Третье задание
Четвертое задание
Ромб-челендж.
Ровный красивый ромб с единицами по краям.
Подходит для любого числа. Красота ограничивается только размерами экрана. У меня, например, наложения начинаются только после 40.
задание №2
Мое решения ромба)
Мой вариант задания №4
вот мое решение ромба:
Вписываюсь в ромб челлендж
Хотите пострадать фигней попробуйте этот код)))
№4:
Задание 3 (консоль интереснее развернуть в полный экран):
Задание 2:
Пришлось потратить 4 с лишним часов, чтобы более-менее разобраться, как сделать 4 задание с возможностью ввода двузначных чисел. Зато получилась звёздочка 😉
(не советую вводить числа, большие 38, ибо так все цифры сбиваются)
Результат:
Код:
Здравствуйте, скажите, пожалуйста, как возвратить значение переменной из while?
додумался только до такого варианта!
тут ромб-челлендж, значит…
Попробовала так, чтобы цифры в обратном порядке шли к центру.
вводить лучше до 9, так как дальше числа едут.
Как сократить код? И как сделать, чтобы числа не ехали, если больше 9?
как выровнить долго думала, а оказалось все очень примитивно.
надо там, где строят пробелы, прописать лишнюю строчку.
для верхнего треугольника
для нижнего треугольника:
так можно делать треугольники из 2значных цифр, с шириной насколько позволяет ide
Получилось 2 варианта решения до 3-значных чисел.
1 вариант базируясь на знаниях полученных до этого урока.
Для красивого вывода используется функция printf() и ввода scanf_s().
2 вариант — используется цикл for() и тернарный оператор.
Для красивого вывода использую функцию std::setw() из библиотеки <iomanip>
спасибо за 2й вариант, только жаль, что не вставлены комментарии к строкам.
Попробовала повторить для убывающего к центру ряда, но полностью не копировала, вышло хуже.
У Вас неплохо получилось:). Можно было в условии while(—rowWide > 0) убрать >0, если изначально число положительное, то по достижении переменной rowWide значения 0 (0 = false), произойдёт выход из цикла. Также, что б ромбик был красивее, можно было в строчке int printNumRow{numRow}; Вместо numRow использовать просто num.
Добавил комменты:)
Второе сделал следующим образом.
Тоже интересная программа с использованием while, если писать самому, то далеко не очевидная. Попробуйте сами!
Задание: Напишите программу используя выкл while, которая просит ввести число от 1 до 9, а потом делает из него ромб чисел, по типу
1
1 2 1
1 2 3 2 1
1 2 1
1
Важно: программа должна выводить числа по уменьшению ( 1 всегда по краю)
УДАЧИ!!!
Из головы не выходит)
Вот как вывести после)
В тесте простые проги, просто немного фантазии, хотя 4й сразу не сделал.
Можно короче (знаю, что лучше функциями):
попыталась, чтобы ромб был ровным и красивым при числах больше 9.
Юра, если захочешь, можешь этот код как задание №5 вывесить. Я его написал в процессе выполнения задания №4 (которое, как ни парадоксально, я не смог осилить:-) Ромб из чисел:
Все разобрался 🙂
Вот решение если кому нужно делал в DEV C++
Порылся вроде должно выглядеть так, но не работает.
Всем, здравствуйте! Извиняюсь за наверное слишком простой вопрос, но может кто помочь сделать такое задание: Написать программу для вычисления заданного выражения и вывода на экран полученного значения, используя циклический оператор While. Выражение: — 0,1+ 0,4 -0,7+…-1,9. Не могу сам сообразить ка это сделать, в программировании ноль но сделать нужно 🙁
вот задание №3:
вот первое задание :
или так:
Задание 3:
Здравствуйте Юрий! Спасибо за такой пост! Все было очень понятно, кроме , посл. примера(треугольник). Можете объяснить каждую строку?!!!
Спасибо за уроки, но я не могу понять как выводиться результат в последнем примере. Если цикл while повторяет себя до выполнения условия, то почему std::cout сробатывает с каждым разом на один раз больше? Почему на последней строчке 5 цифр, хоть и используется только один cout?
Потому что используется инкремент outer:
Как вариант:
Неплохо.
Поделюсь своим вариантом для 4 задачи
Как вариант может быть.
Добрый вечер! Скажите, пожалуйста, на каком уроке и будет ли вообще разбор таких понятий, как printf и scanf?
Привет, printf и scanf в этих уроках не рассматриваются, но, возможно, позднее напишу статью о них (но это точно будет не скоро).