Циклы
Цикл - это блок кода, который выполняется сверху вниз и повторяется с начала, когда достигает конца. Продолжается это дело до тех пор, пока выполняется какое то условие. Есть два основных цикла, с которыми мы будем работать, это for
и while
.
Цикл for
Цикл for
, в простонародии счётчик. При запуске принимает три настройки: инициализация, условие и изменение. Цикл for
обычно содержит переменную, которая изменяется на протяжении работы цикла, а мы можем пользоваться её значением в своих целях.
- Инициализация - здесь обычно присваивают начальное значение переменной цикла. Например:
int i = 0;
- Условие - здесь задаётся условие, при котором выполняется цикл. Как только условие нарушается, цикл завершает работу. Например:
i < 100;
- Изменение - здесь указывается изменение переменной цикла на каждой итерации. Например:
i++;
Пример:
for (int i = 0; i < 100; i++) { // тело цикла }
В теле цикла мы можем пользоваться значением переменной i
, которая примет значения от 0 до 99 на протяжении работы цикла, после этого цикл завершается. Как это использовать? Вспомним предыдущий урок про массивы и рассмотрим пример:
// создадим массив на 100 ячеек byte myArray[100]; for (int i = 0; i < 100; i++) { // заполним все ячейки значением 25 myArray[i] = 25; }
Именно при помощи цикла for
очень часто работают с массивами. Можно сложить все элементы массива для поиска среднего арифметического:
// создадим массив byte myVals[] = {5, 60, 214, 36, 98, 156}; // переменная для хранения суммы int sum = 0; for (int i = 0; i < 6; i++) { // суммируем весь массив в sum sum += myVals[i]; } // разделим sum на количество элементов // и получим среднее арифметическое! sum /= 6; // получилось 94!
Что касается особенностей использования for
в языке C++: любая его настройка является необязательной, то есть её можно не указывать для каких-то особенных алгоритмов. Например вы не хотите создавать переменную цикла, а использовать для этого другую имеющуюся переменную. Пожалуйста! Но не забывайте, что разделители настроек (точка с запятой) обязательно должны присутствовать на своих местах, даже если настроек нет!
int index = 0; // свой счётчик for (; index < 60; index += 10) { // переменная index принимает значения // 0, 10, 20, 30, 40, 50 }
В цикле for можно сделать несколько счётчиков, несколько условий и несколько инкрементов, разделяя их при помощи оператора запятая
, смотрите пример:
// объявить i и j // прибавлять i+1 и j+2 for (byte i = 0, j = 0; i < 10; i++, j += 2) { // тут i меняется от 0 до 9 // и j меняется от 0 до 18 }
Также в цикле может вообще не быть настроек, и такой цикл можно считать вечным:
for (;;) { // выполняется вечно... }
Использование замкнутых циклов не очень приветствуется, но иногда является очень удобным способом поймать какое-то значение, или дать программе "зависнуть" при наступлении ошибки. Выйти из цикла при помощи оператора break
, о нём поговорим чуть ниже.
Цикл "for each"
В свежих версиях компилятора появилась поддержка аналога цикла foreach, который есть в некоторых других языках программирования. Реализация позволяет сократить код для прохода по любому массиву данных. Рассмотрим типичный пример вывода массива чисел в порт, как в уроке про массивы:
int vals[] = {10, 11, 12, 13, 14}; for (int i = 0; i < sizeof(vals); i++) { Serial.println(vals[i]); }
Как это работает: мы завели цикл for
с переменной-счётчиком i
, который меняется от 0
до размера массива, который вы вычисляем через sizeof()
. Внутри цикла мы используем счётчик как индекс массива, чтобы обратиться к каждой его ячейке как [i]
. Но цикл for
для работы с массивом можно записать более красиво:
for (тип_данных_массива переменная : массив) {}
В нашем примере вывода это будет выглядеть так:
int vals[] = {10, 11, 12, 13, 14}; for (int val : vals) { Serial.println(val); }
Как это работает: мы создаём переменную val
такого же типа как массив, а также указываем имя массива через двоеточие. На каждой итерации цикла переменная val
будет принимать значение ячейки массива в порядке от 0 до размера массива с шагом 1. Таким образом мы решили ту же задачу, но написали меньше кода.
Важный момент: на каждой итерации цикла значение ячейки присваивается к переменной val
, то есть фактически мы можем только прочитать значение (через буферную переменную). Для непосредственного доступа к элементам массива нужно создавать ссылку, то есть просто добавить оператор &
int vals[] = {10, 11, 12, 13, 14}; for (int &val : vals) { Serial.println(val); val = 0; }
val
в этом случае предоставляет полный доступ к элементу массива, то есть можно его читать и писать. Пример выше выведет значение каждого элемента, а затем обнулит его. После выполнения цикла весь массив будет забит нулями.
В такой реализации цикла for
у нас нет счётчика, что может быть неудобным для некоторых алгоритмов, но счётчик всегда можно добавить свой. Например забьём массив числами от 0 до 90 с шагом 10:
int vals[10]; int count = 0; for (int &val : vals) { val = count * 10; count++; }
И это будет всё ещё компактнее классического for
.
Оператор break
Оператор break
(англ. "ломать") позволяет выйти из цикла. Использовать его можно как по условию, так и как-угодно-удобно. Например давайте досрочно выйдем из цикла при достижении какого-то значения:
for (int i = 0; i < 100; i++) { // покинуть цикл при достижении 50 if (i == 50) break; }
Или вот такой абстрактный пример, покинем "вечный" цикл при нажатии на кнопку:
for (;;) { if (кнопка нажата) break; }
Выход из цикла является не единственным интересным инструментом, ещё есть оператор пропуска - continue
.
Оператор continue
Оператор continue
(англ. "продолжить") досрочно завершает текущую итерацию цикла и переходит к следующей. Например давайте заполним массив, как делали выше, но пропустим один элемент:
// создадим массив на 100 ячеек byte myArray[100]; for (int i = 0; i < 100; i++) { // если i равно 10, пропускаем if (i == 10) continue; // заполним все ячейки значением 25 myArray[i] = 25; }
Таким образом элемент под номером 10 не получит значения 25, итерация завершится до операции присваивания.
Цикл while
Цикл while
(англ. "пока"), он же "цикл с предусловием", выполняется до тех пор, пока верно указанное условие. Если условие изначально неверно, цикл будет пропущен, не сделает ни одной итерации. Объявляется очень просто: ключевое слово while
, далее условие в скобках, и вот уже тело цикла:
int i = 0; while (i < 10) { i++; }
Хммм, вам не кажется знакомым действие этого примера? Всё верно, это полный аналог цикла for
с настройками (int i = 0; i < 10; i++)
. Единственное отличие в том, что на последней итерации i
примет значение 10
, так как на значении 9
цикл разрешит выполнение. Ещё интересный вариант, который можно встретить на просторах чужого кода. Работает на основе того факта, что любое число кроме нуля обрабатывается логикой как true
:
byte a = 5; while (a--) { // выполнится 5 раз }
Цикл while
тоже удобно использовать как вечный цикл, например, ожидая наступление какого-либо события (нажатие кнопки):
// выполняется, пока не нажата кнопка while (кнопка не нажата);
Пока условие не произойдёт, код не пойдёт дальше, застрянет на этом цикле. Как вы уже поняли, оператор if
тут не нужен, нужно указывать именно логическое значение, можно даже вот так:
while (true);
Всё, вертимся здесь бесконечно! Помимо цикла с предусловием есть ещё цикл с постусловием, так называемый do while
Цикл do while
do while
- "делать пока", работа этого цикла полностью аналогична циклу while
за тем исключением, что здесь условие задаётся после цикла, т.е. цикл выполнится как минимум один раз, затем проверит условие, а не наоборот. Пример:
do { // тело цикла } while (условие);
Где применять? Да где угодно, по ходу написания собственных программ вы почувствуете, где удобнее использовать тот или иной цикл.
Видео
Полезные страницы
- Набор GyverKIT – большой стартовый набор Arduino моей разработки, продаётся в России
- Каталог ссылок на дешёвые Ардуины, датчики, модули и прочие железки с AliExpress у проверенных продавцов
- Подборка библиотек для Arduino, самых интересных и полезных, официальных и не очень
- Полная документация по языку Ардуино, все встроенные функции и макросы, все доступные типы данных
- Сборник полезных алгоритмов для написания скетчей: структура кода, таймеры, фильтры, парсинг данных
- Видео уроки по программированию Arduino с канала “Заметки Ардуинщика” – одни из самых подробных в рунете
- Поддержать автора за работу над уроками
- Обратная связь – сообщить об ошибке в уроке или предложить дополнение по тексту ([email protected])