Урок №67. Цикл while

  Юрий  | 

  |

  Обновл. 4 Сен 2021  | 

 226995

 ǀ   73 

На этом уроке мы детально рассмотрим цикл while, его конструкцию, особенности и использование.

Цикл while

Цикл while является самым простым из четырех циклов, которые есть в языке C++. Он очень похож на ветвление if/else:

while (условие)
    тело цикла;

Цикл while объявляется с использованием ключевого слова while. В начале цикла обрабатывается условие. Если его значением является true (любое ненулевое значение), то тогда выполняется тело цикла.

Однако, в отличие от оператора if, после завершения выполнения тела цикла, управление возвращается обратно к while и процесс проверки условия повторяется. Если условие опять является true, то тогда тело цикла выполняется еще раз.

Например, следующая программа выводит все числа от 0 до 9:

Результат выполнения программы:

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 может и вообще не выполняться, например:

Условие 15 < 10 сразу принимает значение false, и тело цикла пропускается. Единственное, что выведет эта программа:

done!

Бесконечные циклы


С другой стороны, если условие цикла всегда принимает значение true, то и сам цикл будет выполняться бесконечно. Это называется бесконечным циклом. Например:

Поскольку переменная count не увеличивается на единицу в этой программе, то условие count < 10 всегда будет true. Следовательно, цикл никогда не будет завершен, и программа будет постоянно выводить 0 0 0 0 0....

Мы можем преднамеренно объявить бесконечный цикл следующим образом:

Единственный способ выйти из бесконечного цикла — использовать операторы 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 может привести к неожиданным результатам. Например:

Взгляните на эту программу еще раз и постарайтесь найти ошибку.

Оказывается, эта программа представляет собой бесконечный цикл. Она начинается с вывода 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 раз:

Для фундаментальных типов переменных это нормально. Для нефундаментальных типов переменных (таких как структуры или классы) это может сказаться на производительности. Следовательно, нефундаментальные типы переменных лучше определять перед циклом.

Обратите внимание, переменная count объявлена вне тела цикла. Это важно и необходимо, поскольку нам нужно, чтобы значение переменной сохранялось на протяжении всех итераций (не уничтожалось по новой с каждым повтором цикла).

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

Результат выполнения программы:

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
1 2
1 2 3
1 2 3 4
1 2 3 4 5

Тест


Задание №1

Почему в программе, приведенной выше, переменная inner объявлена внутри блока while, а не сразу после объявления переменной outer (вне блока while)?

Ответ №1

Переменная inner объявлена внутри блока while так, чтобы она была восстановлена (и повторно инициализирована значением 1) каждый раз, когда выполняется внешний цикл. Если бы переменная inner была объявлена вне внешнего цикла while, то её значение никогда не было бы сброшено до 1, или нам бы пришлось это сделать самостоятельно с помощью операции присваивания. Кроме того, поскольку переменная inner используется только внутри внешнего цикла while, то имеет смысл объявить её именно там. Помните, что переменные нужно объявлять максимально близко к их первому использованию!

Задание №2

Напишите программу, которая выводит буквы английского алфавита от a до z вместе с кодами из ASCII-таблицы.

Подсказка: Чтобы выводить символы как целые числа — используйте оператор static_cast.

Ответ №2

Задание №3

Измените программу из последнего подраздела «Вложенные циклы while» так, чтобы она выводила следующее:

5 4 3 2 1
4 3 2 1
3 2 1
2 1
1

Ответ №3

Задание №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 (371 оценок, среднее: 4,85 из 5)
Загрузка...

Комментариев: 73

  1. Taras Polishuk:

    Мое решение 2 — й задачи.

  2. denis:

    Моё решение 4 задачи без логических ветвлений. Потратил около 40 минут  на её решение

  3. Сашуня:

    Второе задание:

    Третье задание:

    Четвертео задание:

  4. Владимир:

    Задание 4. Вроде написал так же, долго морочил голову почему получается треугольник. Так и не понял разницы

    1. Valdemar:

      Там где «else»  поставь двойной пробел — »     «, а не одинарный »  «.

  5. Михаил:

    Я написал код, который будет работать правильно даже если размер будет больше 10:

  6. Jo:

    Подскажите пожалуйста.
    Случайно вставил пробел в ' \n' вместо '\n' в строке (std::cout << a << " = " << static_cast<int>(a) << ' \n'; )
    И получил неожиданный и не понятный результат.
    Почему так происходит? Какой принцип работы оператора новой строки?

    1. Алексей:

      Ты используешь одинарные кавычки, а они предназначены для char'ов. Пробел тоже считается за символ.

  7. vlad-024:

    Задание 4. Интересный получился результат, вроде бы код как в ответе, а на выходе что то другое.

    OS Linux, IDE Code::Blocks

    1
    2 1
    3 2 1
    4 3 2 1
    5 4 3 2 1

    1. Александр:

      я думаю в строке 12 нужно добавить пробел и все получится

  8. Евгений:

    2 задание:

  9. Дмитрий:

    // 2 задание

  10. dmkamenev:

    4 задание

    1. Иван:

      Так у тебя ж результат неверный. Сортировка не такая как нужно.

  11. ОЛЕГ-777:

    С большим удовольствием написал эти программы.

    Задание №2:

    main.ccp:

    Задание №3:

    main.ccp:

    Задание 4:

    main.ccp:

    Благодаря этому прекрасному сайту быстро прогрессирую. Прекрасное изложение материала. Огромное спасибо Юрию!!!

  12. Юрий:

    4 задача

  13. Алексей Л.:

    "Следовательно, лучше использовать iii, jjj или kkk в качестве имен для счетчиков."
    Фу, громоздко, и не красиво.

  14. Иван:

    Мой вариант ромбика, получился не большой:)

  15. Иван:

    Задание №2 измененный мой вариант без кодов.

    Задание №4 мой вариант.

  16. Ruslan:

    Ромб)

  17. Ruslan:

    Задание № 4

  18. Ruslan:

    Задание № 3

  19. Ruslan:

    Задание № 2

  20. Вася:

    Вот более простое решение четвертого задания:

    1. foo:

      🙂 шутка

  21. Владислав:

    Ромб-челлендж.
    Идеально ровный, почти красивый ромб.
    Максимальное число (размер ромба) по краям, единица посередине.

  22. Даня:

    Вот мой вариант 4 задания без использования if/else

  23. Onium:

    Третье задание

    Четвертое задание

  24. Максим:

    Ромб-челендж.
    Ровный красивый ромб с единицами по краям.
    Подходит для любого числа. Красота ограничивается только размерами экрана. У меня, например, наложения начинаются только после 40.

  25. Юшка:

    задание №2

  26. Orest:

    Мое решения ромба)

  27. Вадим:

    Мой вариант задания №4

  28. ttshkall:

    вот мое решение ромба:

  29. Марат:

    Вписываюсь в ромб челлендж

  30. Илья:

    Хотите пострадать фигней попробуйте этот код)))

  31. Владимир:

    №4:

  32. Владимир:

    Задание 3 (консоль интереснее развернуть в полный экран):

  33. Владимир:

    Задание 2:

  34. Анастасия Лузинсан:

    Пришлось потратить 4 с лишним часов, чтобы более-менее разобраться, как сделать 4 задание с возможностью ввода двузначных чисел. Зато получилась звёздочка 😉
    (не советую вводить числа, большие 38, ибо так все цифры сбиваются)

    Результат:

    Код:

  35. Кекс:

    Здравствуйте, скажите, пожалуйста, как возвратить значение переменной из while?

  36. Игорь:

    додумался только до такого варианта!

  37. Ivan:

  38. zashiki:

    тут ромб-челлендж, значит…
    Попробовала так, чтобы цифры в обратном порядке шли к центру.
    вводить лучше до 9, так как дальше числа едут.

    Как сократить код? И как сделать, чтобы числа не ехали, если больше 9?

    1. zashiki:

      как выровнить долго думала, а оказалось все очень примитивно.
      надо там, где строят пробелы, прописать лишнюю строчку.
      для верхнего треугольника

      для нижнего треугольника:

      так можно делать треугольники из 2значных цифр, с шириной насколько позволяет ide

    2. Saliwer:

      Получилось 2 варианта решения до 3-значных чисел.
      1 вариант базируясь на знаниях полученных до этого урока.
      Для красивого вывода используется функция printf() и ввода scanf_s().

      2 вариант — используется цикл for() и тернарный оператор.
      Для красивого вывода использую функцию std::setw() из библиотеки <iomanip>

      1. zashiki:

        спасибо за 2й вариант, только жаль, что не вставлены комментарии к строкам.
        Попробовала повторить для убывающего к центру ряда, но полностью не копировала, вышло хуже.

        1. Saliwer:

          У Вас неплохо получилось:). Можно было в условии while(—rowWide > 0) убрать >0, если изначально число положительное, то по достижении переменной rowWide значения 0 (0 = false), произойдёт выход из цикла. Также, что б ромбик был красивее, можно было в строчке int printNumRow{numRow}; Вместо numRow использовать просто num.

          Добавил комменты:)

  39. Алексей:

    Второе сделал следующим образом.

  40. Dima Malovanyy:

    Тоже интересная программа с использованием while, если писать самому, то далеко не очевидная. Попробуйте сами!
    Задание: Напишите программу используя выкл while, которая просит ввести число от 1 до 9, а потом делает из него ромб чисел, по типу

    1
    1 2 1
    1 2 3 2 1
    1 2 1
    1

    Важно: программа должна выводить числа по уменьшению ( 1 всегда по краю)
    УДАЧИ!!!

    1. Алексей:

      Из головы не выходит)
      Вот как вывести после)

      В тесте простые проги, просто немного фантазии, хотя 4й сразу не сделал.

    2. Mirovengil:

      Можно короче (знаю, что лучше функциями):

    3. zashiki:

      попыталась, чтобы ромб был ровным и красивым при числах больше 9.

  41. Максим:

  42. Константин:

    Юра, если захочешь, можешь этот код как задание №5 вывесить. Я его написал в процессе выполнения задания №4 (которое, как ни парадоксально, я не смог осилить:-) Ромб из чисел:

  43. Владислав:

    Все разобрался 🙂
    Вот решение если кому нужно делал в DEV C++

  44. Владислав:

    Порылся вроде должно выглядеть так, но не работает.

  45. Владислав:

    Всем, здравствуйте! Извиняюсь за наверное слишком простой вопрос, но может кто помочь сделать такое задание: Написать программу для вычисления заданного выражения и вывода на экран полученного значения, используя циклический оператор While. Выражение: — 0,1+ 0,4 -0,7+…-1,9. Не могу сам сообразить ка это сделать, в программировании ноль но сделать нужно 🙁

  46. Вячеслав:

    вот задание №3:

  47. Вячеслав:

    вот первое задание :

  48. Alexey:

    или так:

  49. kmish:

  50. Алексей:

    Задание 3:

  51. Adel:

    Здравствуйте Юрий! Спасибо за такой пост! Все было очень понятно, кроме , посл. примера(треугольник). Можете объяснить каждую строку?!!!

  52. Игорь:

  53. Tairak:

    Спасибо за уроки, но я не могу понять как выводиться результат в последнем примере. Если цикл while повторяет себя до выполнения условия, то почему std::cout сробатывает с каждым разом на один раз больше? Почему на последней строчке 5 цифр, хоть и используется только один cout?

    1. Фото аватара Юрий:

      Потому что используется инкремент outer:

  54. Денис:

    Как вариант:

    1. Фото аватара Юрий:

      Неплохо.

  55. master114:

    Поделюсь своим вариантом для 4 задачи

    1. Фото аватара Юрий:

      Как вариант может быть.

  56. Николай:

    Добрый вечер! Скажите, пожалуйста, на каком уроке и будет ли вообще разбор таких понятий, как printf и scanf?

    1. Фото аватара Юрий:

      Привет, printf и scanf в этих уроках не рассматриваются, но, возможно, позднее напишу статью о них (но это точно будет не скоро).

  57. Максим:

Добавить комментарий

Ваш E-mail не будет опубликован. Обязательные поля помечены *