Menu

Функции в C++: объявление, определение и вызов (с примерами)

Как писать функции в C++ — анатомия из типа возвращаемого значения/имени/параметров, объявления против определений, возврат значений, void-функции и подводные камни вроде пропущенного return или забытого прототипа.

На этой странице есть исполняемые редакторы: меняйте, запускайте и сразу видите результат.

Зачем нужны функции

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

Вы всё это время уже вызывали одну функцию — main. Это точка входа, с которой стартует любая программа на C++. Теперь вы напишете свои собственные. Циклы, которые вы видели раньше, например цикл for по диапазону, часто живут внутри функций, чтобы вы могли повторно использовать целый кусок логики по имени.

Анатомия функции

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

int add(int a, int b) {   // тип возвращаемого значения | имя | параметры
    return a + b;         // тело
}
  • int — это тип возвращаемого значения, то есть тип значения, которое функция отдаёт назад.
  • add — имя, по которому вы её вызываете.
  • (int a, int b) — параметры, входные данные, которые предоставляет вызывающий код.
  • В фигурных скобках находится тело — код, который выполняется при вызове.

Вот это в законченной программе. Определение функции над main означает, что main видит её, когда вызывает.

Вызов add(2, 3) запускает функцию с a = 2 и b = 3, и всё выражение становится возвращённым значением. Вы можете сохранить его в переменной или использовать прямо внутри другого выражения, как это делает вторая строка с cout.

Возврат значения

Оператор return делает две вещи: возвращает значение вызывающему коду и немедленно завершает функцию. Любой код после return не выполняется — управление сразу же возвращается туда, откуда функция была вызвана.

Тип возвращаемого значения должен совпадать с объявленным типом возврата (или приводиться к нему). Функция, объявленная как int, должна возвращать int; не вернуть ничего или выйти за конец без return — это ошибка для любой не-void-функции.

void-функции

Не каждая функция выдаёт значение. Когда функция просто что-то делает — печатает вывод, обновляет состояние, — её тип возвращаемого значения равен void. void-функция может использовать простое return;, чтобы выйти заранее, либо просто доработать до закрывающей фигурной скобки.

Попытка использовать результат void-функции (int x = greet("Ada");) — это ошибка компиляции, потому что присваивать нечего. Частая ошибка — написать return someValue; внутри void-функции; компилятор тоже это отвергает.

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

C++ читает файл сверху вниз, поэтому по умолчанию функция должна стоять перед кодом, который её вызывает. Когда такой порядок неудобен, вы разделяете функцию на объявление (его также называют прототипом) и определение.

Объявление указывает сигнатуру функции и заканчивается точкой с запятой — без тела. Оно обещает компилятору: «эта функция существует; вот как её вызывать». Полное определение может прийти позже, даже после main.

Без прототипа в строке 4 компилятор наткнулся бы на square(5) внутри main до того, как когда-либо увидел square, и сборка завершилась бы ошибкой. Прототипы — это также способ, которым заголовочные файлы позволяют многим файлам исходного кода использовать одни и те же функции. Обратите внимание, что имена параметров в объявлении необязательны: int square(int); работает ничуть не хуже; для компилятора важны только типы.

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

Несколько ловушек снова и снова подстерегают новичков:

  • Вызов до объявления. Если вы получаете ошибку «add was not declared in this scope», значит, функция определена ниже своего первого вызова и не имеет прототипа. Перенесите определение выше или добавьте прототип.
  • Забыть про return. Достижение конца не-void-функции без returnнеопределённое поведение: вызывающий код получает мусор. Компилируйте с включёнными предупреждениями (-Wall), и компилятор это укажет.
  • Путать определение и вызов. У определения есть тело в фигурных скобках и нет завершающей точки с запятой. У объявления есть точка с запятой и нет тела. Их путаница — например, поставить точку с запятой сразу после списка параметров функции, которую вы собирались определить, — порождает запутанные ошибки.
  • Игнорировать возвращаемое значение. add(2, 3); отдельной строкой компилируется, но вычисленная сумма молча отбрасывается. Убедитесь, что вы действительно используете то, что возвращает функция.
// Выглядит как определение, но лишняя ; превращает это в
// объявление, за которым следует осиротевший блок — частая опечатка:
int triple(int n);   // <- эта ; завершает оператор
{
    return n * 3;    // n здесь не определён; этот блок теперь осиротел
}

Далее: параметры функций

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

Часто задаваемые вопросы

Как написать функцию в C++?

Задайте ей тип возвращаемого значения, имя, круглые скобки для параметров и тело в фигурных скобках: int add(int a, int b) { return a + b; }. Вызывайте её по имени с аргументами, например add(2, 3). Если функция ничего не возвращает, используйте void в качестве типа возвращаемого значения.

В чём разница между объявлением и определением функции в C++?

Объявление (или прототип) сообщает компилятору имя функции, тип возвращаемого значения и параметры и заканчивается точкой с запятой: int add(int a, int b);. Определение дополнительно содержит тело в фигурных скобках. Вы можете объявить функцию до main, а определить её позже — объявление позволяет вызывать её ещё до того, как появится определение.

Что произойдёт, если функция C++ не вернёт значение?

Для void-функции ничего — она просто завершается. Но для не-void-функции достижение конца без return — это неопределённое поведение: вызывающий код получает мусорное значение, и программа может работать неправильно. Большинство компиляторов предупреждают об этом; всегда возвращайте значение на каждом пути не-void-функции.

Coddy programming languages illustration

Учитесь программировать с Coddy

НАЧАТЬ