Проверьте с помощью компьютера какое значение вернет функция

Обновлено: 03.07.2024

Функции можно объявить, чтобы они возвращали ссылочный тип. Существует две причины создания такого объявления.

Возвращаемая информация представляет собой настолько крупный объект, что возврат ссылки является более эффективным, чем возврат копии.

Тип функции должен представлять собой l-значение.

Объект, на который указывает ссылка, не выйдет из области видимости при возврате управления функцией.

Так же, как можно повысить эффективность передачи больших объектов в функции по ссылке, также может быть более эффективным возврат больших объектов из функций по ссылке. Протокол возврата ссылок устраняет необходимость копировать объект во временное хранилище перед возвратом.

Типы возврата ссылок также могут оказаться полезными, если результатом функции должно быть l-значение. Большинство перегруженных операторов относятся к этой категории, в частности оператор присваивания. Перегруженные операторы рассматриваются в перегруженных операторах.

Пример

Рассмотрим пример Point .

Выходные данные

Обратите внимание, что функции x и y объявляются как типы возвращаемых ссылок. Эти функции можно использовать на любой стороне оператора присваивания.

Также обратите внимание, что в функции main объект ThePoint остается в области видимости, поэтому его ссылочные элементы продолжают действовать, и к ним можно получить безопасный доступ.

Объявления ссылочных типов должны содержать инициализаторы. Исключение составляют следующие случаи.

Явное extern объявление

Объявление члена класса

Объявление в классе

Объявление аргумента в адрес функции или типа возвращаемого значения для функции

Предупреждение при возвращении адреса локальной переменной

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

Компилятор выдает предупреждение в этом случае: warning C4172: returning address of local variable or temporary . В простых программах доступ может случайно сохраниться, если ссылка будет использована вызывающим объектом до перезаписи соответствующей области памяти. Однако это чистая случайность. Обратите внимание на предупреждение.

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

Функции

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

Функция, в которой находится вызов, называется caller, а функция, которую вызывают — вызываемая функция, например:

// Объявление функции doPrint(), которую мы будем вызывать doPrint ( ) ; // прерываем выполнение функции main() вызовом функции doPrint(). Функция main() в данном случае является caller-ом

Результат выполнения программы:

Starting main()
In doPrint()
Ending main()

Эта программа начинает выполнение с первой строки функции main(), где выводится на экран следующая строка: Starting main() . Вторая строка функции main() вызывает функцию doPrint(). На этом этапе выполнение стейтментов в функции main() приостанавливается и процессор переходит к выполнению стейтментов внутри функции doPrint(). Первая (и единственная) строка в doPrint() выводит текст In doPrint() . Когда процессор завершает выполнение doPrint(), он возвращается обратно в main() к той точке, на которой остановился. Следовательно, следующим стейтментом является вывод строки Ending main() .

Обратите внимание, для вызова функции нужно указать её имя и список параметров в круглых скобках () . В примере, приведенном выше, параметры не используются, поэтому круглые скобки пусты. Мы детально поговорим о параметрах функций на следующем уроке.

Правило: Не забывайте указывать круглые скобки () при вызове функций.

Возвращаемые значения

Когда функция main() завершает свое выполнение, она возвращает целочисленное значение обратно в операционную систему, используя оператор return.

Затем, внутри вызываемой функции, мы используем оператор return, чтобы указать возвращаемое значение — какое именно значение будет возвращаться обратно в caller.

Рассмотрим простую функцию, которая возвращает целочисленное значение:

// int означает, что функция возвращает целочисленное значение обратно в caller // Эта функция возвращает целочисленное значение, поэтому мы должны использовать оператор return return7 ( ) ; // возвращаемое значение 7 игнорируется, так как функция main() ничего с ним не делает

Результат выполнения программы:

Первый вызов функции return7() возвращает 7 обратно в caller, которое затем передается в std::cout для вывода.

Второй вызов функции return7() опять возвращает 7 обратно в caller. Выражение 7 + 3 имеет результат 10 , который затем выводится на экран.

Третий вызов функции return7() опять возвращает 7 обратно в caller. Однако функция main() ничего с ним не делает, поэтому ничего и не происходит (возвращаемое значение игнорируется).

Примечание: Возвращаемые значения не выводятся на экран, если их не передать объекту std::cout. В последнем вызове функции return7() значение не отправляется в std::cout, поэтому ничего и не происходит.

Тип возврата void

Функции могут и не возвращать значения. Чтобы сообщить компилятору, что функция не возвращает значение, нужно использовать тип возврата void. Взглянем еще раз на функцию doPrint() из вышеприведенного примера:

// Эта функция не возвращает никакого значения, поэтому оператор return здесь не нужен

Эта функция имеет тип возврата void, который означает, что функция не возвращает значения. Поскольку значение не возвращается, то и оператор return не требуется.

Вот еще один пример использования функции типа void:

// Эта функция не возвращает никакого значения, поэтому оператор return здесь не нужен returnNothing ( ) ; // функция returnNothing() вызывается, но обратно в main() ничего не возвращает std :: cout << returnNothing ( ) ; // ошибка, эта строчка не скомпилируется. Вам нужно будет её закомментировать

В первом вызове функции returnNothing() выводится Hi! , но ничего не возвращается обратно в caller. Точка выполнения возвращается обратно в функцию main(), где программа продолжает свое выполнение.

Возврат значений функцией main()

Теперь у вас есть понимание того, как работает функция main(). Когда программа выполняется, операционная система делает вызов функции main() и начинается её выполнение. Стейтменты в main() выполняются последовательно. В конце функция main() возвращает целочисленное значение (обычно 0 ) обратно в операционную систему. Поэтому main() объявляется как int main() .

Почему нужно возвращать значения обратно в операционную систему? Дело в том, что возвращаемое значение функции main() является кодом состояния, который сообщает операционной системе об успешном или неудачном выполнении программы. Обычно, возвращаемое значение 0 (ноль) означает что всё прошло успешно, тогда как любое другое значение означает неудачу/ошибку.

Обратите внимание, по стандартам языка C++ функция main() должна возвращать целочисленное значение. Однако, если вы не укажете return в конце функции main(), компилятор возвратит 0 автоматически, если никаких ошибок не будет. Но рекомендуется указывать return в конце функции main() и использовать тип возврата int для функции main().

Еще о возвращаемых значениях

Во-первых, если тип возврата функции не void, то она должна возвращать значение указанного типа (использовать оператор return). Единственно исключение — функция main(), которая возвращает 0 , если не предоставлено другое значение.

Во-вторых, когда процессор встречает в функции оператор return, он немедленно выполняет возврат значения обратно в caller и точка выполнения также переходит в caller. Любой код, который находится за оператором return в функции — игнорируется.

Функция может возвращать только одно значение через return обратно в caller. Это может быть либо число (например, 7 ), либо значение переменной, либо выражение (у которого есть результат), либо определенное значение из набора возможных значений.

Но есть способы обойти правило возврата одного значения, возвращая сразу несколько значений, но об этом детально мы поговорим на соответствующем уроке.

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

Повторное использование функций

Одну и ту же функцию можно вызывать несколько раз, даже в разных программах, что очень полезно:

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

Хотя эта программа достаточно тривиальна, и нам не нужно разбивать ее на несколько функций, но что, если бы мы захотели это сделать? Получение целочисленного значения от пользователя – это четко определенная задача, которую мы хотим, чтобы наша программа выполняла, поэтому она могла бы стать хорошим кандидатом на функцию.

Итак, давайте напишем программу для этого:

Хотя эта программа – хорошая попытка решения, она не совсем работает.

Когда вызывается функция 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 , и программа продолжает выполнение.

Тип возврата 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 в отдельных строках.

Эта программа напечатает 5 дважды (в отдельных строках). Оба раза, когда вызывается функция getNumbers() , возвращается значение 5. Когда выполняется инструкция return 5; , функция немедленно завершается, поэтому инструкция return 7; никогда не выполняется.

Эта программа не будет компилироваться, потому что функция имеет недопустимое имя. Мы говорили о правилах именования в уроке «1.7 – Ключевые слова и именование идентификаторов».

1h) Чуть сложнее.

Эта программа будет скомпилирована, но функция не будет вызвана, потому что при вызове функции отсутствуют круглые скобки. То, что на самом деле будет выведено, зависит от компилятора.

Вопрос 2

Что означает «DRY» и почему это полезно?

DRY означает «Don’t Repeat Yourself» (не повторяйся). Это практика, которая включает в себя написание кода таким образом, чтобы минимизировать избыточность. Это делает ваши программы более краткими, менее подверженными ошибкам и более удобными для поддержки.

Для нас в этом курсе имеется ещё один важный момент. Посмотрим внимательнее на возвращаемое значение функций. Некоторые функции не возвращают существенное значение после завершения, но некоторые возвращают, и важно понимать что это за значение и как использовать его в своём коде и как сделать так чтобы ваши собственные функции возвращали полезные значения. Мы объясним всё это ниже.

Базовая компьютерная грамотность, знание основ HTML и CSS, JavaScript first steps, Functions — reusable blocks of code.

Что из себя представляют возвращаемые значения?

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

Мы уже видели этот блок кода в нашей первой статье про функции. Мы вызываем функцию replace() на строке myText и передаём ей 2 параметра — заменяемую подстроку и подстроку, которой будем заменять. Когда функция завершит выполнение, она вернёт значение, которым является новая строка со сделанными в ней заменами. В коде выше мы сохраняем это возвращаемое значение как значение переменной newString .

Если вы посмотрите на функцию replace() на MDN reference page, вы увидите секцию под названием Return value. Очень важно знать и понимать какие значения возвращаются функциями, так что мы пытаемся включать эту информацию везде, где это возможно.

Некоторые функции не возвращают значения( на наших reference pages, возвращаемое значение обозначено как void или undefined в таких случаях). Например, в функции displayMessage() которую мы сделали в прошлой статье, в результате выполнения функции не возвращается никакого значения. Функция всего лишь отображает что-то где-то на экране.

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

Использование возвращаемых значений в ваших собственных функциях

Чтобы вернуть значение своей функции, вы должны использовать ключевое слово return. Мы видели это в действии недавно - в нашем примере random-canvas-circles.html. Наша функция draw() отрисовывает где-то на экране 100 случайных кружков.

Внутри каждой итерации есть 3 вызова функции random() . Это сделано чтобы сгенерировать случайное значение для текущей координаты x, y и для радиуса. Функция random() принимает 1 параметр (целое число) и возвращает случайное число в диапазоне от 0 до этого числа. Выглядит это вот так:

Тоже самое может быть написано вот так:

Но первую версию написать быстрее и она более компактна.

Мы возвращаем результат вычисления Math.floor(Math.random()*number) каждый раз когда функция вызывается. Это возвращаемое значение появляется в момент вызова функции и код продолжается. Так, например, если мы выполним следующую строчку:

и 3 вызова random() вернут значения 500, 200 и 35, соответственно, строчка будет выполнена как если бы она была такой:

Сначала выполняются вызовы функции random() , на место которых подставляются возвращаемые ей значения, а затем выполнятся сама строка.

Активное обучение: наша собственная, возвращающая значение функция

Теперь напишем нашу собственную возвращающую значение функцию.

  1. Первым делом, сделайте локальную копию файла function-library.html из GitHub. Это простая HTML страничка, содержащая текстовое поле <input> и параграф Также там есть элемент <script> в котором мы храним в 2ух переменных ссылки на оба HTML-элемента. Это маленькая страничка позволит вам ввести число в text box и отобразит различные, относящиеся к нему числа в параграфе ниже.
  2. Теперь добавим несколько полезных функций в элемент <script> . Ниже двух существующих строчек JavaScript, добавьте следующие описания функций:

Здесь мы создаём обработчик событий onchange который срабатывает когда меняется когда новое значение вводится в text input и подтверждается (введите значение и, например, нажмите tab). Когда анонимная функция срабатывает, введённое в input значение сохраняется в переменной num .

Замечание: Если у вас проблемы с работой данного примера, не стесняйтесь сверить ваш код с работающей версией finished version on GitHub (или смотрите живой пример), или спросите нас.

К этому моменту мы хотели бы чтобы вы написали парочку собственных функций и добавили их в библиотеку. Как на счёт квадратного или кубического корня числа или длины окружности круга с длиной радиуса равной числу num ?

Это упражнение привнесло парочку важных понятий в изучении того, как использовать ключевое слово return . В дополнение:

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

Заключение

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

Если в статье есть что-то что вы не поняли, не стесняйтесь перечитать статью ещё раз или свяжитесь с нами для получения помощи.

Читайте также: