Функция (function) — именованный фрагмент программы: получает данные через параметры, выполняет тело и может вернуть результат в место вызова.

Зачем нужно

Без функций одно и то же действие — найти минимум, ввести массив — приходится копировать в каждое место, где оно нужно. Функция описывает действие один раз, а дальше его применяют к разным данным по имени.

Как работает

int Min(int x, int y) {  // тип результата, имя, параметры
  return x < y ? x : y;  // тело
}
 
int value = Min(10, 20);  // вызов: аргументы 10 и 20
  • Параметры — переменные в заголовке функции (x, y).
  • Аргументы — выражения в скобках вызова (10, 20, a + 5).
  • Вызов — выражение: его значение и есть возвращённый результат, поэтому std::cout << Min(10, 20) работает без промежуточной переменной.

Как значения аргументов попадают в параметры — копией, через адрес или через ссылку — см. Передача параметров.

Объявление и определение

Вызвать функцию можно, только если её имя уже объявлено выше. Объявление без тела — int Min(int x, int y); — сообщает компилятору тип результата и типы параметров, имена параметров в нём необязательны. Определение содержит тело. Объявлений может быть сколько угодно, определение — одно (ODR). Если функцию объявили и вызвали, но не определили, сборка падает на этапе компоновки. Подробнее — Объявление и определение.

Сигнатура

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

  • возвращаемый тип — int Min(int, int) и float Min(int, int) конфликтуют;
  • имена параметров;
  • const на параметре, передаваемом по значению: int Min(int x, const int y) — та же Min(int, int);
  • аргументы по умолчанию.

На сигнатуре держится Перегрузка функций: одно имя, разные списки типов.

Возврат

return завершает вызов и, если нужно, передаёт результат. Срабатывает тот return, до которого управление дошло первым. Если функция с не-void типом дошла до конца тела без return — это Неопределённое поведение. Только у main конец тела равносилен return 0;.

Функция, результат которой не нужен, имеет тип void. Она может дойти до конца тела или выйти раньше через return;.

Что происходит при вызове

На машинном уровне вызов — это передача аргументов (через регистры и/или память), выполнение тела, передача результата и возврат управления по адресу возврата. Данные одного вызова, лежащие в стеке, — кадр стека вызовов (stack frame): адрес возврата, аргументы, локальные переменные, сохранённые регистры. Вложенный вызов кладёт новый кадр поверх, возврат его снимает. Состав кадра зависит от платформы и оптимизаций, иногда кадра нет вовсе. Из-за кадров глубокая Рекурсия может переполнить стек.

Тонкости

  • Аргументы по умолчанию (int Min(int x, int y = 1);) позволяют опускать только последние аргументы. Значение задаётся один раз — обычно в объявлении, которое видит вызывающий код, — и не повторяется в определении. Новых функций они не создают.
  • Имена функций на курсе пишутся CamelCase, кроме main, см. Кодстайл курса.
  • main вызывать из своего кода стандарт запрещает.
  • Локальная переменная функции с static живёт всю программу и сохраняет значение между вызовами, см. Область действия.
  • Функция может вернуть ссылку, но не на свою локальную переменную, см. Висячий указатель.

Частые ошибки

  • Вызвать функцию выше её объявления.
  • Ждать, что return что-то напечатает.
  • Забыть return на одном из путей выполнения.
  • Различать функции только возвращаемым типом.
  • Повторить аргумент по умолчанию в определении.
  • Считать, что совпадение имён аргумента и параметра их связывает.