Способы возврата значения из функции
Вернуть значение из функции позволяет оператор return . После исполнения этого оператора выполнение функции останавливается и управление передается обратно в точку вызова функции. Это означает, что инструкции после оператора return никогда не будут выполнены. Если внутри функции нет оператора return , то по достижении закрывающей фигурной скобки управление будет передано в точку вызова функции. В этом случае возвращаемое значение не определено.
Если функция не возвращает никакого значения, то вместо типа данных в объявлении и определении функции указывается ключевое слово void . Внутри такой функции оператора return может не быть, однако его можно использовать без указания значения для преждевременного выхода из функции. Вызов функции, не возвращающей никакого значения, нельзя размещать внутри какой-либо инструкции. Только в отдельной инструкции. Пример функции, которая не возвращает никакого значения:
В остальных случаях в объявлении и определении функции перед названием функции задается возвращаемый тип данных. Значение этого типа должно быть указано в операторе return . В этом случае возвращается копия значения. Функция может вернуть значение любого типа, кроме массива. Работать с массивами необходимо через параметры функции, передавая указатель на него, или возвращая указатель на конкретный элемент. Вызов функции, возвращающей какое-либо значение, можно разместить внутри выражения с правой стороны от оператора = . Если функция возвращает ссылку, то допускается размещение функции и с левой стороны от оператора = . Возвращаемое значение можно присвоить переменной или просто проигнорировать. Пример функции, возвращающей копию значения:
При использовании оператора return не должно быть неоднозначных ситуаций. Например, в этом случае возвращаемое значение зависит от условия:
Если переменная x имеет значение больше нуля, то все будет нормально, но если переменная равна нулю или имеет отрицательное значение, то возвращаемое значение не определено и функция вернет произвольное значение, так называемый «мусор». В этом случае при компиляции выводится предупреждающее сообщение: warning: control reaches end of non-void function . Чтобы избежать подобной ситуации, следует в конце тела функции вставить оператор return со значением по умолчанию:
Функция может возвращать указатель. В этом случае в объявлении и определении функции указывается соответствующий тип указателя. В качестве примера вернем указатель на последний символ строки или нулевой указатель, если строка пустая (листинг 12.22).
Листинг 12.22. Возврат указателя
В языке C++ функции могут возвращать также ссылки. В этом случае допускается размещение функции как с правой, так и с левой стороны от оператора = . Чтобы функция возвращала ссылку необходимо в объявлении и определении после типа данных указать оператор & . В качестве примера напишем функцию, которая позволяет получить или изменить значение элемента массива по указанному индексу (листинг 12.23).
Листинг 12.23. Возврат ссылки
Учебник C++ (Qt Creator и MinGW) в формате PDF
Помощь сайту
ПАО Сбербанк:
Счет: 40817810855006152256
Реквизиты банка:
Наименование: СЕВЕРО-ЗАПАДНЫЙ БАНК ПАО СБЕРБАНК
Корреспондентский счет: 30101810500000000653
БИК: 044030653
КПП: 784243001
ОКПО: 09171401
ОКОНХ: 96130
Скриншот реквизитов
Почему функция «возвращает» значение?

Изучающие программирование часто удивляются: почему функция «возвращает» результат? Почему именно такое странное слово — «возврат»? Как будто мы сначала даём ей результат, а потом она его возвращает обратно.
Понятно, что это не так. Мы даём функции аргументы (а иногда и вообще не даём ничего), а она даёт нам в ответ какую-то информацию… А иногда не даёт! Тогда мы говорим «она ничего не возвратила».
Дело в том, что return означает не возврат информации, а возврат управления.
При вызове функции текущий код становится на паузу и передаёт управление в функцию. Функция делает свои дела и возвращает управление в то место, где происходил вызов. И заодно может отправить какую-то информацию туда.
Что за управление? Управление той штукой, которая исполняет код. Грубо говоря — управление компьютером. Код передаёт функции пульт управления компьютером, а функция потом возвращает его обратно.
Мы привыкли читать подобный код как «функция возвращает значение answer », но правильнее будет читать «функция возвращает управление в место вызова и отправляет туда значение answer».
Именно поэтому внутри функции возможен такой код:
Здесь функция не «возвращает ничего», а «возвращает управление в место вызова и не отправляет туда никакую информацию».

Конечно, такая формулировка хоть и технически более корректна, в реальной жизни нет смысла отказываться от «возвращает значение». Это, можно сказать, общепринятое сокращение.
Полезная информация Начните изучать разработку с бесплатного курса «Основы современной вёрстки». Вы научитесь создавать статические веб-страницы, стилизовать элементы, использовать редакторы кода с полезными расширениями. В конце курса вы опубликуете свой первый сайт на GitHub Pages.
2.2 – Возвращаемые значения функций
Эта программа состоит из двух концептуальных частей: во-первых, мы получаем значение от пользователя. Затем мы сообщаем пользователю, чему равно это значение, умноженное на два.
Хотя эта программа достаточно тривиальна, и нам не нужно разбивать ее на несколько функций, но что, если бы мы захотели это сделать? Получение целочисленного значения от пользователя – это четко определенная задача, которую мы хотим, чтобы наша программа выполняла, поэтому она могла бы стать хорошим кандидатом на функцию.
Итак, давайте напишем программу для этого:
Хотя эта программа – хорошая попытка решения, она не совсем работает.
Когда вызывается функция getValueFromUser , пользователя просят ввести целое число, как и ожидалось. Но введенное им значение теряется, когда getValueFromUser завершает работу и управление возвращается к main . Переменная num никогда не инициализируется значением, введенным пользователем, поэтому программа всегда печатает ответ 0.
Чего нам не хватает, так это того, чтобы getValueFromUser могла вернуть значение, введенное пользователем, обратно в main , чтобы main могла использовать эти данные.
Возвращаемые значения
Когда вы пишете пользовательскую функцию, вы можете определить, будет ли ваша функция возвращать значение вызывающей стороне или нет. Чтобы вернуть значение вызывающей стороне, необходимы две вещи.
Во-первых, ваша функция должна указать, значение какого типа будет возвращено. Это делается путем установки типа возвращаемого значения функции, который является типом, определенным перед именем функции. В приведенном выше примере функция getValueFromUser имеет тип возвращаемого значения void , а функция main имеет тип возвращаемого значения int . Обратите внимание, что это не определяет, какое конкретное значение будет возвращено – только тип значения.
Во-вторых, внутри функции, которая будет возвращать значение, мы используем инструкцию return , чтобы указать конкретное значение, возвращаемое вызывающей стороне. Конкретное значение, возвращаемое функцией, называется возвращаемым значением. Когда инструкция return выполняется, возвращаемое значение копируется из функции обратно в вызывающую функцию. Этот процесс называется возвратом по значению.
Давайте рассмотрим простую функцию, которая возвращает целочисленное значение, и пример программы, которая ее вызывает:
При запуске эта программа печатает:
Выполнение начинается с верхней части main . В первой инструкции вычисляется вызов функции returnFive , в результате чего вызывается функция returnFive . Функция returnFive возвращает конкретное значение 5 обратно вызывающей стороне, которое затем выводится в консоль через std::cout .
Во второй инструкции вычисляется вызов функции returnFive , что приводит к повторному вызову функции returnFive . Функция returnFive возвращает значение 5 обратно вызывающей стороне. Выражение 5 + 2 вычисляется для получения результата 7, который затем выводится в консоль через std::cout .
В третьей инструкции функция returnFive вызывается снова, в результате чего значение 5 возвращается обратно вызывающей стороне. Однако функция main ничего не делает с возвращаемым значением, поэтому больше ничего не происходит (возвращаемое значение игнорируется).
Примечание. Возвращаемые значения не будут напечатаны, если вызывающая функция не отправит их в консоль через std::cout . В последнем случае, в примере выше, возвращаемое значение не отправляется в std::cout , поэтому ничего не печатается.
Исправляем нашу тестовую программу
Теперь, узнав это, мы можем исправить программу, которую представили в начале урока:
Когда эта программа выполняется, первая инструкция в main создаст переменную типа int с именем num . Когда программа перейдет к инициализации num , она увидит, что есть вызов функции getValueFromUser , поэтому она выполнит эту функцию. Функция getValueFromUser просит пользователя ввести значение, а затем возвращает это значение вызывающей функции ( main ). Это возвращенное значение используется как значение для инициализации переменной num .
Скомпилируйте эту программу и запустите ее несколько раз, чтобы убедиться, что она работает.
Отсутствие возвращаемого значения
Функции не обязаны возвращать значение. Чтобы сообщить компилятору, что функция не возвращает значение, используется тип возвращаемого значения void . Давайте посмотрим на функцию doPrint() из предыдущего урока:
Эта функция имеет тип возвращаемого значения void , что указывает на то, что она не возвращает значение вызывающей стороне. Поскольку она не возвращает значение, инструкция return не требуется (попытка вернуть конкретное значение из функции с типом возврата void приведет к ошибке компиляции).
Вот еще один пример функции, ничего не возвращающей, и пример программы, которая ее вызывает:
При первом вызове функции returnNothing функция печатает « Hi », а затем ничего не возвращает вызывающей функции. Управление возвращается в main , и программа продолжает выполнение.
Второй вызов функции returnNothing даже не будет компилироваться. Функция returnNothing имеет возвращаемый тип void , то есть не возвращает значения. Однако эта инструкция пытается отправить возвращаемое из returnNothing значение в std::cout для печати. std::cout не знает, что с этим делать (какое значение он будет выводить?). Следовательно, компилятор пометит это как ошибку. Вам нужно закомментировать эту строку кода, чтобы код компилировался.
Тип возврата void (что означает, что ничего не возвращается) используется, когда нам нужна функция, которая ничего не возвращает вызывающей стороне (потому что в этом нет необходимости). В приведенном выше примере у функции returnNothing есть полезное поведение (она печатает « Hi »), но ей не нужно ничего возвращать вызывающей стороне (в данном случае main ). Следовательно, функции returnNothing присваивается возвращаемый тип void .
Возвращаясь к main
Теперь у вас есть концептуальные инструменты, чтобы понять, как на самом деле работает функция main . Когда программа выполняется, операционная система вызывает функцию main . Затем выполнение переходит в начало main . Инструкции в main выполняются последовательно. Наконец, main возвращает целочисленное значение (обычно 0), и ваша программа завершается. Значение, возвращаемое из main , иногда называют кодом состояния (также иногда называемым кодом выхода или, реже, кодом возврата), поскольку оно используется, чтобы указать, успешно ли была выполнена программа.
По определению, код состояния 0 означает, что программа выполнена успешно.
Лучшая практика
Ваша функция main должна возвращать 0, если программа работает нормально.
Код состояния, отличный от нуля, часто используется для обозначения сбоя (и хотя он отлично работает в большинстве операционных систем, строго говоря, его портируемость не гарантируется).
Для продвинутых читателей
Стандарт C++ определяет значение только трех кодов состояния: 0, EXIT_SUCCESS и EXIT_FAILURE . 0 и EXIT_SUCCESS означают, что программа выполнена успешно. EXIT_FAILURE означает, что программа не была успешно выполнена.
EXIT_SUCCESS и EXIT_FAILURE определены в заголовочном файле <cstdlib> :
Если вы хотите максимизировать портируемость, вы должны использовать только 0 или EXIT_SUCCESS , чтобы указать на успешное завершение, или EXIT_FAILURE , чтобы указать на неудачное завершение.
C++ запрещает явный вызов функции main .
На данный момент вы также должны определять свою функцию main внизу исходного файла кода, под другими функциями.
Несколько дополнительных замечаний о возвращаемых значениях
Во-первых, если функция имеет тип возврата, являющийся не- void , она должна возвращать значение этого типа (используя инструкцию return ). Несоблюдение этого правила приведет к неопределенному поведению. Единственное исключение из этого правила – функция main() , которая, если возвращаемое значение не указано явно, примет, что оно равно 0. Тем не менее, рекомендуется явно возвращать значение из main , как для демонстрации вашего намерения, так и для согласованности с другими функциями (что не позволит вам опустить возвращаемое значение).
Лучшая практика
Всегда явно указывайте возвращаемое значение для любой функции, которая имеет тип возврата не- void .
Предупреждение
Неспособность вернуть значение из функции с типом возврата не- void (кроме main ) приведет к неопределенному поведению.
Во-вторых, когда выполняется инструкция return , функция немедленно, в этой точке, возвращается в вызывающую функцию. Любой дополнительный код в функции игнорируется.
В-третьих, функция может возвращать вызывающей функции при каждом вызове только одно значение. Однако это значение не обязательно должно быть литералом, оно может быть результатом любого допустимого выражения, включая переменную или даже вызов другой функции, которая возвращает значение. В приведенном выше примере getValueFromUser() мы вернули переменную, содержащую число, введенное пользователем.
Наконец, обратите внимание, что функция может определять, что означает ее возвращаемое значение. Некоторые функции используют возвращаемые значения как коды состояния, чтобы указать, были ли они успешными или неудачными. Другие функции возвращают вычисленное или выбранное значение. Остальные функции ничего не возвращают. Что возвращает конкретная функция, и смысл этого значения определяется автором функции. Из-за большого разнообразия возможностей рекомендуется задокументировать функцию комментарием, указывающим, что означают возвращаемые значения.
Повторное использование функций
Теперь мы можем проиллюстрировать хороший случай повторного использования функции. Рассмотрим следующую программу:
В этой программе мы дважды вызываем getValueFromUser : один раз для инициализации переменной x и один раз для инициализации переменной y . Это избавляет нас от дублирования кода для ввода пользовательских данных и снижает вероятность ошибки. Как только мы узнаем, что getValueFromUser работает для одной переменной, она будет работать для любого их количества, сколько нам будет нужно.
В этом суть модульного программирования: возможность написать функцию, протестировать ее, убедиться, что она работает, а затем знать, что мы можем повторно использовать ее столько раз, сколько захотим, и она будет продолжать работать (пока мы не изменим эту функцию – после чего нам придется ее повторно протестировать).
Лучшая практика
Следуйте рекомендациям DRY: «не повторяйся». Если вам нужно сделать что-то более одного раза, подумайте, как изменить свой код, чтобы удалить как можно больше избыточности. Переменные можно использовать для хранения результатов вычислений, которые необходимо использовать более одного раза (чтобы нам не нужно было повторять вычисления). Функции можно использовать для определения последовательности инструкций, которые мы хотим выполнять более одного раза. А циклы (которые мы рассмотрим в следующей главе) можно использовать для выполнения инструкции более одного раза.
Заключение
Возвращаемые значения позволяют функциям возвращать одно значение обратно вызывающей функции.
Функции позволяют минимизировать избыточность наших программ.
Небольшой тест
Вопрос 1
Проверьте следующие программы и укажите, что они выводят, или будут ли они не компилироваться.
Эта программа печатает число 16.
Эта программа не компилируется. Вложенные функции не допускаются.
Эта программа компилируется, но ничего не выводит. Значения, возвращаемые функциями, ни для чего не используются (и, таким образом, отбрасываются).
Эта программа печатает буквы A и B в отдельных строках.
Эта программа не компилируется. Функция printA() возвращает void , которое main() пытается отправить в std::cout . Это приведет к ошибке компиляции.
Эта программа напечатает 5 дважды (в отдельных строках). Оба раза, когда вызывается функция getNumbers() , возвращается значение 5. Когда выполняется инструкция return 5; , функция немедленно завершается, поэтому инструкция return 7; никогда не выполняется.
Эта программа не будет компилироваться, потому что функция имеет недопустимое имя. Мы говорили о правилах именования в уроке «1.7 – Ключевые слова и именование идентификаторов».
1h) Чуть сложнее.
Эта программа будет скомпилирована, но функция не будет вызвана, потому что при вызове функции отсутствуют круглые скобки. То, что на самом деле будет выведено, зависит от компилятора.
Вопрос 2
Что означает «DRY» и почему это полезно?
DRY означает «Don’t Repeat Yourself» (не повторяйся). Это практика, которая включает в себя написание кода таким образом, чтобы минимизировать избыточность. Это делает ваши программы более краткими, менее подверженными ошибкам и более удобными для поддержки.
Функции в языке Си
Функция — это самостоятельная единица программы, которая спроектирована для реализации конкретной подзадачи.
Функция является подпрограммой, которая может содержаться в основной программе, а может быть создана отдельно (в библиотеке). Каждая функция выполняет в программе определенные действия.
Сигнатура функции определяет правила использования функции. Обычно сигнатура представляет собой описание функции, включающее имя функции, перечень формальных параметров с их типами и тип возвращаемого значения.
Семантика функции определяет способ реализации функции. Обычно представляет собой тело функции.
Определение функции
Каждая функция в языке Си должна быть определена, то есть должны быть указаны:
- тип возвращаемого значения;
- имя функции;
- информация о формальных аргументах;
- тело функции.
Определение функции имеет следующий синтаксис:
Пример : Функция сложения двух вещественных чисел
В указанном примере возвращаемое значение имеет тип float . В качестве возвращаемого значения в вызывающую функцию передается значение переменной y . Формальными аргументами являются значения переменных x и z .
Если функция не возвращает значения, то тип возвращаемого значения для нее указывается как void . При этом операция return может быть опущена. Если функция не принимает аргументов, в круглых скобках также указывается void .
Различают системные (в составе систем программирования) и собственные функции.
Системные функции хранятся в стандартных библиотеках, и пользователю не нужно вдаваться в подробности их реализации. Достаточно знать лишь их сигнатуру. Примером системных функций, используемых ранее, являются функции printf() и scanf() .
Собственные функции — это функции, написанные пользователем для решения конкретной подзадачи.
Разбиение программ на функции дает следующие преимущества:
- Функцию можно вызвать из различных мест программы, что позволяет избежать повторения программного кода.
- Одну и ту же функцию можно использовать в разных программах.
- Функции повышают уровень модульности программы и облегчают ее проектирование.
- Использование функций облегчает чтение и понимание программы и ускоряет поиск и исправление ошибок.
С точки зрения вызывающей программы функцию можно представить как некий «черный ящик», у которого есть несколько входов и один выход. С точки зрения вызывающей программы неважно, каким образом производится обработка информации внутри функции. Для корректного использования функции достаточно знать лишь ее сигнатуру.
Вызов функции
Общий вид вызова функции
Фактический аргумент — это величина, которая присваивается формальному аргументу при вызове функции. Таким образом, формальный аргумент — это переменная в вызываемой функции, а фактический аргумент — это конкретное значение, присвоенное этой переменной вызывающей функцией. Фактический аргумент может быть константой, переменной или выражением. Если фактический аргумент представлен в виде выражения, то его значение сначала вычисляется, а затем передается в вызываемую функцию. Если в функцию требуется передать несколько значений, то они записываются через запятую. При этом формальные параметры заменяются значениями фактических параметров в порядке их следования в сигнатуре функции.
Возврат в вызывающую функцию
По окончании выполнения вызываемой функции осуществляется возврат значения в точку ее вызова. Это значение присваивается переменной, тип которой должен соответствовать типу возвращаемого значения функции. Функция может передать в вызывающую программу только одно значение. Для передачи возвращаемого значения в вызывающую функцию используется оператор return в одной из форм:
Действие оператора следующее: значение выражения, заключенного в скобки, вычисляется и передается в вызывающую функцию. Возвращаемое значение может использоваться в вызывающей программе как часть некоторого выражения.
Оператор return также завершает выполнение функции и передает управление следующему оператору в вызывающей функции. Оператор return не обязательно должен находиться в конце тела функции.
Функции могут и не возвращать значения, а просто выполнять некоторые вычисления. В этом случае указывается пустой тип возвращаемого значения void , а оператор return может либо отсутствовать, либо не возвращать никакого значения:
Пример : Посчитать сумму двух чисел.
Результат выполнения 
В языке Си нельзя определять одну функцию внутри другой.
В языке Си нет требования, чтобы семантика функции обязательно предшествовало её вызову. Функции могут определяться как до вызывающей функции, так и после нее. Однако если семантика вызываемой функции описывается ниже ее вызова, необходимо до вызова функции определить прототип этой функции, содержащий:
- тип возвращаемого значения;
- имя функции;
- типы формальных аргументов в порядке их следования.
Прототип необходим для того, чтобы компилятор мог осуществить проверку соответствия типов передаваемых фактических аргументов типам формальных аргументов. Имена формальных аргументов в прототипе функции могут отсутствовать.
Если в примере выше тело функции сложения чисел разместить после тела функции main, то код будет выглядеть следующим образом:
Рекурсивные функции
Функция, которая вызывает сама себя, называется рекурсивной функцией .
Рекурсия — вызов функции из самой функции.
Пример рекурсивной функции — функция вычисления факториала.
Результат выполнения 
Более подробно рекурсивные функции рассмотрены в этой статье.
Математические функции
Математические функции хранятся в стандартной библиотеке math.h . Аргументы большинства математических функций имеют тип double . Возвращаемое значение также имеет тип double .
Углы в тригонометрических функциях задаются в радианах.