Функция (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на одном из путей выполнения. - Различать функции только возвращаемым типом.
- Повторить аргумент по умолчанию в определении.
- Считать, что совпадение имён аргумента и параметра их связывает.