3.06. Функции.md


Ваши программы для Рудирона уже умеют работать с данными, принимать решения, повторять действия и хранить наборы значений в массивах. Но когда код становится длинным, он превращается в «пасту» из команд, в которой сложно разобраться. А что, если одну и ту же задачу, например, мигание светодиодом, нужно выполнить в разных частях программы? Копировать код - плохая идея.

Для решения этих проблем в C++ существует один из самых важных инструментов — функции. Функции позволяют нам упаковать блок кода в именованную «коробку», которую можно вызывать снова и снова. Это ключ к созданию чистого, организованного и эффективного кода.

Что такое функция?

Функция - это именованный блок кода, который выполняет определённую задачу.

Аналогия. Представьте, что вы пишете кулинарную книгу. Вместо того чтобы в каждом рецепте подробно расписывать, как приготовить тесто для пиццы, вы выносите этот процесс в отдельный рецепт под названием «Приготовить тесто». Теперь в любом другом рецепте вы можете просто написать: «Возьмите одну порцию теста (см. рецепт «Приготовить тесто»)».

Функция в программировании - это и есть такой «рецепт». Вы один раз описываете, как выполнить какое-то действие (мигнуть светодиодом, рассчитать расстояние, проверить датчик), а затем можете «вызывать» это действие из любой части программы, просто указав имя функции.

Функции помогают:

  • Разбить большую программу на маленькие, логически завершённые части.
  • Избежать повторения кода (принцип DRY — Don’t Repeat Yourself).
  • Упростить отладку и внесение изменений (исправив ошибку в одном месте - в теле функции, вы исправите её везде, где эта функция вызывается).

Создание (определение) функции

Прежде чем использовать функцию, её нужно создать, или, как говорят программисты, определить. Определение функции - это и есть написание того самого «рецепта».

Синтаксис определения функции:

возвращаемыйТип имяФункции(параметры) {
  // Тело функции: команды, которые нужно выполнить
}

Давайте разберём эту структуру:

  • возвращаемыйТип. Тип данных, которые функция «вернёт» в качестве результата своей работы (например, int, float). Если функция просто выполняет действие и ничего не возвращает, используется специальный тип void.
  • имяФункции. Имя, которое вы придумываете для своего «рецепта» (например, blinkLed, calculateDistance).
  • параметры. Данные, которые мы передаём в функцию для работы. Это как ингредиенты для рецепта. Они перечисляются в круглых скобках (). Если функции не нужны входные данные, скобки остаются пустыми.
  • { } (тело функции). Блок кода в фигурных скобках, который и будет выполняться при каждом вызове функции.

Пример (функция, которая ничего не возвращает):

// Определяем функцию, которая мигает светодиодом
// Она принимает два "ингредиента": номер пина и количество миганий
void blinkLed(int pin, int times) {
  for (int i = 0; i < times; i++) {
    digitalWrite(pin, HIGH);
    delay(500);
    digitalWrite(pin, LOW);
    delay(500);
  }
}

void setup() {
  pinMode(LED_BUILTIN_1, OUTPUT);
  
  // Вызываем нашу функцию, чтобы мигнуть светодиодом L1 три раза
  blinkLed(LED_BUILTIN_1, 3);
}

void loop() {
  // loop() пуст, так как всё действие происходит в setup()
}

В этом примере мы создали удобную функцию blinkLed(), которую теперь можем использовать для мигания любым светодиодом любое количество раз.

Вызов функции и возвращаемые значения

Чтобы наш «рецепт» был исполнен, его нужно вызвать. Вызов функции - это просто указание её имени в том месте кода, где нужно выполнить её действия. Если функция принимает параметры (ингредиенты), мы должны передать их в круглых скобках.

Некоторые функции не просто выполняют действия, но и возвращают результат. Например, функция может что-то вычислить и «отдать» нам итоговое число. Для этого используется команда return.

Пример (функция, которая возвращает значение).
Представим, что нам часто нужно переводить температуру из градусов Фаренгейта в градусы Цельсия. Создадим для этого функцию.

// Эта функция принимает температуру в Фаренгейтах (тип float)
// и возвращает результат тоже в виде float
float fahrenheitToCelsius(float fahrenheit) {
  // Выполняем расчёт
  float celsius = (fahrenheit - 32.0) * 5.0 / 9.0;
  // Возвращаем результат
  return celsius;
}

void setup() {
  Serial.begin(9600);
  
  float tempF = 77.0; // Температура в комнате +77°F
  
  // Вызываем функцию и сохраняем её результат в новую переменную
  float tempC = fahrenheitToCelsius(tempF); 
  
  Serial.print(tempF);
  Serial.print(" градусов по Фаренгейту - это ");
  Serial.print(tempC);
  Serial.println(" градусов по Цельсию."); // Выведет: ...это 25.00 градусов...
}

void loop() {}

В этом примере функция fahrenheitToCelsius() выполняет вычисления и с помощью return передаёт результат обратно в setup(), где мы сохраняем его в переменную tempC.

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

Функции - это ключ к созданию понятных, гибких и надёжных программ. Они позволяют:

  • Структурировать код: Вместо одной длинной loop() у вас будет набор коротких и понятных функций, каждая из которых отвечает за свою задачу (readSensors(), moveRobot(), updateDisplay()).
  • Переиспользовать код: Написав функцию один раз, вы можете вызывать её сколько угодно раз в разных частях проекта.
  • Упростить отладку: Если что-то работает не так, гораздо проще найти и исправить ошибку в маленькой, изолированной функции, чем в огромном блоке кода.

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


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