Перейти к основному содержимому

Конструкция ветвления

Один робот, условие и два взаимоисключающих варианта движения

Робот получает разные данные и должен выбирать разные действия. Если путь свободен — ехать, если впереди препятствие — остановиться или повернуть. В C++ такой выбор записывают конструкциями if, else if и else. Они направляют выполнение по одной из предусмотренных ветвей.

Коротко

if выполняет блок, когда условие истинно. Связанный else выполняет альтернативный блок, когда условие ложно. Цепочка else if проверяет варианты сверху вниз и выбирает первую истинную ветвь. Сравнения дают значение типа bool, а логические операторы объединяют несколько признаков.

Зачем это нужно

Без ветвления скетч повторяет одинаковые команды независимо от окружающей среды. Условия превращают измерения и команды в поведение:

  • кнопка нажата — начать учебный тест;
  • расстояние меньше порога — остановиться;
  • оба датчика линии видят фон — искать линию;
  • заряд ниже заданного уровня — запретить движение;
  • пришёл символ остановки — выключить команды приводам.

На hero один упрощённый робот передаёт результат датчика в ромб решения. Оранжевый и мятный пути обозначают взаимоисключающие результаты одной проверки: остановиться либо продолжить маршрут вокруг препятствия. Это алгоритмические линии, не электрическая схема.

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

Главная идея

Условие — выражение, которое даёт true или false. Если оно истинно, выполняется тело if. Иначе программа пропускает этот блок и, если предусмотрен else, выполняет альтернативу.

const int distanceCm = 14;

void setup() {
Serial.begin(9600);
}

void loop() {
if (distanceCm < 20) {
Serial.println("stop");
} else {
Serial.println("move");
}

delay(500);
}

Здесь в каждом проходе выбирается ровно одна из двух ветвей. После выбранного блока выполнение продолжается со следующей инструкцией — delay(500).

Результат условияВыполняетсяНе выполняется
trueблок ifсвязанный блок else
falseсвязанный блок elseблок if

Сравнения

Показание датчика обычно сравнивают с порогом, режим — с кодом, а счётчик — с пределом. Результат сравнения имеет тип bool.

ОператорЧитаетсяПример
==равноmode == 2
!=не равноcommand != 'x'
<меньшеdistanceCm < stopDistanceCm
<=меньше или равноvoltageMv <= lowVoltageMv
>большеtemperatureC > limitC
>=больше или равноsampleCount >= requiredSamples

Оператор == сравнивает, а = присваивает. Ошибка особенно коварна, если присваивание допустимо в условном выражении:

if (mode = 2) {  // неверный замысел: mode изменяется
Serial.println("test");
}

Компилятор с предупреждениями часто обращает внимание на такую запись, но полагаться только на предупреждение нельзя. Осмысленная форма выглядит так:

if (mode == 2) {
Serial.println("test");
}

Граница принадлежит одной стороне

Условия < и <= ведут себя по-разному на пороге. Для остановки ближе 20 см нужно заранее решить, что происходит ровно при 20:

distanceCmdistanceCm < 20distanceCm <= 20
19truetrue
20falsetrue
21falsefalse

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

Измерения датчика могут быть недостоверными. Число 0 иногда означает очень близкий объект, а иногда — отсутствие корректного результата; это зависит от устройства и библиотеки. Сначала проверяют достоверность, затем сравнивают физическую величину.

Несколько вариантов: else if

Когда режимов больше двух, создают цепочку:

const int distanceCm = 32;

void setup() {
Serial.begin(9600);
}

void loop() {
if (distanceCm < 15) {
Serial.println("stop");
} else if (distanceCm < 40) {
Serial.println("slow");
} else {
Serial.println("normal");
}

delay(500);
}

Условия проверяются сверху вниз. При distanceCm = 10 истинны оба сравнения < 15 и < 40, но выполняется только первый подходящий блок. Если поставить < 40 раньше, ветвь < 15 станет недостижимой.

ДиапазонПервая истинная проверкаДействие
меньше 15 смdistanceCm < 15остановка
15–39 смdistanceCm < 40 после ложного первого условиямедленный режим
40 см и большени одно условие не истиннообычный режим

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

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

Логические операторы

Несколько признаков соединяют так:

  • && — логическое И: истинны обе части;
  • || — логическое ИЛИ: истинна хотя бы одна часть;
  • ! — логическое НЕ: результат меняется на противоположный.
const bool startPressed = true;
const bool coverClosed = true;
const bool emergencyStopActive = false;

const bool startAllowed =
startPressed && coverClosed && !emergencyStopActive;

void setup() {
Serial.begin(9600);

if (startAllowed) {
Serial.println("start_allowed");
} else {
Serial.println("start_blocked");
}
}

void loop() {
}
ВыражениеКогда истинноПример для робота
a && bистинны a и bстарт нажат и крышка закрыта
`ab`
!aa ложноаварийный стоп не активен

Когда в выражении смешаны && и ||, добавляйте круглые скобки, отражающие замысел. Они уменьшают зависимость чтения от знания приоритетов:

const bool mustStop =
obstacleDetected || (lowBattery && !atChargingStation);

Сокращённое вычисление

Операторы && и || вычисляют слева направо с пропуском ненужной правой части. Для a && b выражение b не вычисляется, если a уже ложно. Для a || b правая часть не вычисляется, если a уже истинно.

Это позволяет сначала проверить возможность операции:

const int sampleCount = 3;
const int selectedIndex = 2;
const int samples[] = {34, 28, 19};

void setup() {
Serial.begin(9600);

if (selectedIndex >= 0 &&
selectedIndex < sampleCount &&
samples[selectedIndex] < 20) {
Serial.println("close_obstacle");
}
}

void loop() {
}

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

Блоки и вложенность

Фигурные скобки явно ограничивают ветвь:

if (sensorValid) {
Serial.println("measurement_ok");
Serial.println(distanceCm);
}

C++ разрешает один оператор без скобок, но следующая добавленная строка уже окажется вне условия. В скетчах робота используйте скобки даже для коротких ветвей.

Вложенные if допустимы, но глубокая лестница усложняет проверку. Часто легче сначала вычислить именованные логические признаки, затем построить плоскую цепочку решений:

const bool measurementValid = distanceCm > 0;
const bool obstacleClose = measurementValid && distanceCm < 20;

Приоритет безопасной реакции

В цепочке первая истинная ветвь побеждает, поэтому порядок должен отражать приоритет. Отказ датчика проверяют раньше обычной команды движения:

const bool emergencyStopActive = false;
const bool sensorValid = true;
const int distanceCm = 18;

void setup() {
Serial.begin(9600);
}

void loop() {
if (emergencyStopActive) {
Serial.println("stop_emergency");
} else if (!sensorValid) {
Serial.println("stop_invalid_sensor");
} else if (distanceCm < 20) {
Serial.println("stop_obstacle");
} else {
Serial.println("move_allowed");
}
}

Этот программный порядок сам по себе не заменяет аппаратную аварийную защиту. Он делает поведение скетча предсказуемым при известных входах.

Шум около порога и гистерезис

Если измерение колеблется около одного порога, команда может быстро переключаться. Один способ снизить дрожание — использовать разные границы остановки и возобновления:

bool movementAllowed = true;
const int stopBelowCm = 20;
const int resumeAboveCm = 25;

void updateMovementState(int distanceCm) {
if (movementAllowed && distanceCm < stopBelowCm) {
movementAllowed = false;
} else if (!movementAllowed && distanceCm > resumeAboveCm) {
movementAllowed = true;
}
}

После остановки на 19 см значение 21 см ещё не разрешит движение: нужно превысить 25 см. Выбор порогов зависит от шума, динамики устройства и требований безопасности; универсальной пары нет.

Что запомнить о ветвлении

  • if выбирает блок по логическому условию.
  • else относится к ближайшему подходящему if и задаёт альтернативу.
  • Цепочка else if выбирает первую истинную ветвь сверху вниз.
  • == сравнивает, = присваивает.
  • Граничные значения проверяют отдельно.
  • &&, || и ! объединяют признаки и используют сокращённое вычисление.
  • Приоритет ветвей должен соответствовать приоритету реакций робота.

Практика

  1. Напишите три режима движения по расстоянию: остановка, медленное движение и обычное. Самостоятельно задайте пороги и составьте таблицу значений ниже, на и выше каждой границы.
  2. Добавьте bool sensorValid. Недостоверное измерение должно выбирать отдельную безопасную ветвь раньше проверки расстояния.
  3. Составьте разрешение запуска из кнопки, закрытой крышки и неактивного аварийного сигнала. Проверьте все восемь сочетаний без готовой таблицы ответов.
  4. Реализуйте гистерезис с двумя порогами и подайте последовательность значений около границы. Запишите моменты смены состояния.

Проверьте себя

  1. Какой тип имеет результат сравнения?
  2. Чем == отличается от =?
  3. Когда выполняется блок else?
  4. Почему порядок else if меняет результат?
  5. Какие значения нужно проверить около порога?
  6. Когда правая часть && не вычисляется?
  7. Зачем нужны фигурные скобки вокруг короткой ветви?
  8. Как два порога уменьшают переключения около шумной границы?
Ориентиры для самопроверки

Сначала ответьте без подсказки. Ответ можно считать полным, если вы:

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

Если один из пунктов объяснить не получается, найдите соответствующую главу статьи, перечитайте её и повторите ответ.

Словарь статьи

ТерминЗначение
условиевыражение, приводимое к логическому результату
ветвлениевыбор пути дальнейшего выполнения
ifконструкция выполнения блока при истинном условии
else ifследующая проверка в упорядоченной цепочке
elseальтернативный блок, когда предыдущие связанные условия ложны
сравнениеоперация, возвращающая bool по отношению двух значений
логический оператор&&, `
сокращённое вычислениепропуск правой части, когда результат уже известен
границазначение, на котором меняется результат условия
гистерезисразные пороги перехода в состояние и выхода из него

Связанные темы

Источники