Как вы помните,
вызов функции с помощью оператора ()
представляет собой выражение. Все
выражения имеют значения, и инструкция
return служит
для определения значения, возвращаемого
функцией. Это значение становится
значением выражения вызова функции.
Инструкция return
имеет следующий синтаксис:
return
выражение;
Инструкция return
может располагаться только в теле
функции. Присутствие ее в любом другом
месте является синтаксической ошибкой.
Когда выполняется инструкция return,
вычисляется выражение и его значение
возвращается в качестве значения
функции.
Инструкция return
прекращает исполнение функции, даже
если в теле функции остались другие
инструкции. Инструкция return
используется для возвращения значения
следующим образом:
function square(x) { return
x*x; }
Инструкция return
может также использоваться без выражения,
тогда она просто прерывает исполнение
функции, не возвращая значение. Например:
function
display_object(obj) {
// Сначала убедимся
в корректности нашего аргумента
// В случае
некорректности пропускаем остаток
функции
if (obj == null) return;
// Здесь находится
оставшаяся часть функции…
}
Если в функции
выполняется инструкция return
без выражения или если выполнение
функции прекращается по причине
достижения конца тела функции, значение
выражения вызова функции оказывается
неопределенным (undefined).
JavaScript вставляет
точку с запятой автоматически, поэтому
нельзя разделять переводом строки
инструкцию return
и следующее за ней выражение.
Инструкция throw
Исключение – это
сигнал, указывающий на возникновение
какой-либо исключительной ситуации или
ошибки. Генерация исключения (throw)
– это способ просигнализировать о такой
ошибке или исключительной ситуации.
Перехватить
исключение (catch),
значит, обработать его, т. е. предпринять
действия, необходимые или подходящие
для восстановления после исключения.
В JavaScript исключения генерируются в тех
случаях, когда возникает ошибка времени
выполнения, тогда программа явно
генерирует его с помощью инструкции
throw.
Исключения перехватываются с помощью
инструкции try/catch/finally.
Инструкция throw
имеет следующий синтаксис:
throw
выражение;
Результатом
выражения может быть значение любого
типа. Однако обычно это объект Error
или экземпляр одного из подклассов
Error.
Также бывает удобно использовать в
качестве выражения строку, содержащую
сообщение об ошибке или числовое
значение, обозначающее некоторый код
ошибки. Вот пример кода, в котором
инструкция throw
применяется для генерации исключения:
function factorial(x) {
// Если входной
аргумент не является допустимым,
// генерируем
исключение!
if (x < 0) throw new
Error(«x не может быть отрицательным»);
// В противном
случае вычисляем значение и нормальным
образом выходим
// из функции
for(var f = 1; x > 1; f
*= x, x—) /* пустое тело цикла */ ;
return f;
}
Когда генерируется
исключение, интерпретатор JavaScript
немедленно прерывает нормальное
исполнение программы и переходит к
ближайшему обработчику исключений.
В обработчиках
исключений используется конструкция
catch инструкции
try/catch/finally.
Если блок кода, в котором возникло
исключение, не имеет соответствующей
конструкции catch,
интерпретатор анализирует следующий
внешний блок кода и проверяет, связан
ли с ним обработчик исключений. Это
продолжается до тех пор, пока обработчик
не будет найден. Если исключение
генерируется в функции, не содержащей
инструкции try/catch/finally,
предназначенной для его обработки, то
исключение распространяется на код,
вызвавший функцию. Так исключения
распространяются по лексической
структуре методов JavaScript вверх по стеку
вызовов. Если обработчик исключения
так и не будет найден, исключение
рассматривается как ошибка и о ней
сообщается пользователю.
Инструкция throw
стандартизована в ECMAScript v3 и реализована
в JavaScript 1.4. Класс Error и его подклассы
также являются частью стандарта
ECMAScript v3, но они не были реализованы до
JavaScript 1.5.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
Все программы имеют жизненный цикл. Когда приложение завершает работу, оно должно сообщить об этом системе. Для передачи соответствующей информации используются специальные операторы – операторы возврата.
Далее предстоит разобраться с принципами работы функции return в C++. Эта информация пригодится преимущественно начинающим разработчикам.
Описание функции возврата
Все пользовательские функции после завершения выполнения инструкций способны возвращать значения туда, откуда они были вызваны. Пример – int main. Она в конце работы всегда возвращает значение в операционную систему. Связано это с тем, что ОС изначально вызывала main для работы программного обеспечения.
В C++ и C имеются операторы возврата. Они называются функциями перехода. Используются для возврата значений или остановки выполнения операции. Называется оператор перехода return (ретурном).
При обработке return происходит следующее:
- Выполнение имеющейся функции прекращается.
- Управление переходит обратно в точку вызова обработанной операции.
- Инструкции, написанные после return, никогда не выполняются.
- Если внутри функции отсутствует соответствующий оператор, при достижении закрывающейся фигурной скобки управление передается в точку вызова обработанной операции. Возвращаемое значение не определяется.
Операторы return имеются несколько сценариев использования. Всего их два. Далее они будут рассмотрены более подробно.
Использование внутри основной функции
Это – первый метод применения returning. Он делает следующее:
- Return останавливает исполнение программы. Происходит присваивание значения 0 (zero) или 1 (one). Они указываются на статус выполнения.
- Соответствующие коды использовались в C в виде соглашения. Связано это с отсутствием поддержки объектов, классов и исключений непосредственно в языке.
- Return 0 возвращает функции 0. Данная запись указывает на успешное выполнение заданных операций. Функция обработана так, как было задумано изначально.
- Return 1 возвращает 1. Указывает на наличие ошибки или сбоя при выполнении приложения. Данная запись свидетельствует о том, что функция не работает по задуманному предназначению.
Что делает returned 0, понятно. Стоит обратить внимание на то, что операционные системы могут самостоятельно завершать приложения с определенными кодами статуса выхода. Это возможно тогда, когда выполняется некоторый спектр недопустимых операций. А еще в зависимости от операционной системы на устройстве пользователя могут меняться соглашения о кодах возврата. Это достаточно редкая ситуация.
Внутри пользовательских функций
Второй вариант использования returns в программном коде – это внутри пользовательских функций. В C++ данный подход широко используется на практике. Соответствующий язык программирования рассматривает логическое значение в качестве отдельного типа данных с двумя «исходами» — истина (true) и ложь (false).
Значения 0 и 1 являются целочисленными (тип int). Они не могут быть неявно преобразованы в логические. Это приводит к тому, что:
- return 0 делает возврат false из заданной инструкции;
- return 1 возвращает истину.
Необходимо помнить, что не всегда returns применяются с 0 и 1. Далее предстоит рассмотреть наглядный пример с отличающимся возвращаемым параметром.
Принцип работы – пример
Чтобы лучше разбираться в операторах перехода в C++, рекомендуется рассмотреть принцип их работы на наглядном примере. Необходимо написать приложение, которое будет запрашивать у клиента число, а затем выводить его квадрат. Для уточнения «цифры» используется отдельная инструкция. Ее предстоит создать самостоятельно.
Делается это так:
- Сначала требуется создать функцию, которая запрашивает число на вывод с консоли:
.
- Во второй строке создается функция типа int под названием EnterNumer.
- Строки 3-6 используются для описания инструкций, выполняемых после вызова функции. Сначала клиенту на экране предлагается ввод целого числа. Далее программа создает переменную для него и получит заданный параметр.
- Описанная функция вызывается из главной (main):
.
- Функция, запрашивающая и получающая число, вызвана. По завершению работы она должна вернуть в main число, которое было указано пользователем. Эта операция прописана в 7 строке кода. После его обработки произойдет возврат переменной int. Поэтому инструкция выглядит как return input:
.
- Чтобы сохранить значения, необходимо воспользоваться переменной. Она инициализируется функцией EnterNumber(). Это происходит в строке 11:
.
- Соответствующая инструкция указывает на то, что в памяти резервируется место для переменной с именем num типа int. Далее происходит инициализация ее значением, возвращенным из функции EnterNumber. Предварительно запускается выполнение предусматриваемых инструкций.
- Необходимо завершить приложение выводом на дисплей квадрата числа, полученного из функции EnterNumber(). Для этого используется std::cout:
.
При обработке соответствующего кода на экране появится такой результат:
Мы сделали программу, которая использует return. Он используется для возврата значений. Но соответствующий оператор может ничего не возвращать.
Тип void
Тип функции void в C++ — это ничего или пустота. Он указывает компилятору на то, что соответствующая команда не возвращает никаких параметров. Если в ее конце устанавливается оператор return, программа просто не пройдет стадию компиляции.
Данная операция вызывается так же, как и команды, возвращающие параметры. Она просто выполняет все необходимые инструкции, написанные в теле, после чего завершается. Вот наглядный пример:
Написанное приложение печатает две строчки текста, хранимых в двух «алгоритмах» типа void. Они ничего не вернут – только выполняют заданные инструкции. После этого происходит после обращения к ним main().
Как быстрее разобраться с темой
Операторы перехода в C++ – это не так трудно, как кажется. Чтобы надписи типа return, include, main и другие не вызывали вопросов, рекомендуется пройти дистанционные компьютерные курсы. На них с нуля пользователей научат разрабатывать приложения на разных языках – от Python до C++.
Пример – от образовательного центра OTUS. Пользователя в срок до 12 месяцев научат основам разработки. Люди смогут освоить любую IT-профессию. В процессе обучения гарантируется постоянное кураторство, много практики, а также интересные домашние задания. Ученику помогут составить портфолио. В конце курса выдается электронный сертификат, который подтверждает приобретенный спектр навыков и знаний.
Хотите освоить современную IT-специальность? Огромный выбор курсов по востребованным IT-направлениям есть в Otus! Ниже – один из них:
From Wikipedia, the free encyclopedia
In computer programming, a return statement causes execution to leave the current subroutine and resume at the point in the code immediately after the instruction which called the subroutine, known as its return address. The return address is saved by the calling routine, today usually on the process’s call stack or in a register. Return statements in many programming languages allow a function to specify a return value to be passed back to the code that called the function.
Overview[edit]
In C and C++, return exp;
(where exp
is an expression) is a statement that tells a function to return execution of the program to the calling function, and report the value of exp
. If a function has the return type void, the return statement can be used without a value, in which case the program just breaks out of the current function and returns to the calling one.[1][2] Similar syntax is used in other languages including Modula-2[3] and Python.[4]
In Pascal there is no return statement. Functions or procedures automatically return when reaching their last statement. The return value from a function is provided within the function by making an assignment to an identifier with the same name as the function.[5] However, some versions of Pascal provide a special function Exit(exp);
that can be used to return a value immediately from a function, or, without parameters, to return immediately from a procedure.[6]
Like Pascal, FORTRAN II, Fortran 66, Fortran 77, and later versions of Fortran specify return values by an assignment to the function name, but also have a return statement; that statement does not specify a return value and, for a function, causes the value assigned to the function name to be returned.[5][7][8]
In some other languages a user defined output parameter is used instead of the function identifier.[9]
Oberon (Oberon-07) has a return clause instead of a return statement. The return clause is placed after the last statement of the procedure body.[10]
Some expression-oriented programming language, such as Lisp, Perl and Ruby, allow the programmer to omit an explicit return statement, specifying instead that the last evaluated expression is the return value of the subroutine. In other cases a Null value is returned if there is no explicit return statement: in Python, the value None
is returned when the return statement is omitted,[4] while in JavaScript the value undefined
is returned.
In Windows PowerShell all evaluated expressions which are not captured (e.g., assigned to a variable, cast to void or piped to $null) are returned from the subroutine as elements in an array, or as a single object in the case that only one object has not been captured.
In Perl, a return value or values of a subroutine can depend on the context in which it was called. The most fundamental distinction is a scalar context where the calling code expects one value, a list context where the calling code expects a list of values and a void context where the calling code doesn’t expect any return value at all. A subroutine can check the context using the wantarray
function. A special syntax of return without arguments is used to return an undefined value in scalar context and an empty list in list context. The scalar context can be further divided into Boolean, number, string, and various reference types contexts. Also, a context-sensitive object can be returned using a contextual return sequence, with lazy evaluation of scalar values.
Many operating systems let a program return a result (separate from normal output) when its process terminates; these values are referred to exit statuses. The amount of information that can be passed this way is quite limited, in practice often restricted to signalling success or fail. From within the program this return is typically achieved by calling Exit (system call) (common even in C, where the alternative mechanism of returning from the main function is available).
Syntax[edit]
Return statements come in many shapes. The following syntaxes are most common:
Language | Return statement | If value omitted, return |
---|---|---|
Ada, Bourne shell,[a] C, C++, Java, PHP, C#, JavaScript, D |
return value; |
In the Bourne shell, exit value of the last command executed in the function
In C[1] and C++,[2] undefined behavior if function is value-returning In PHP,[12] returns In Javascript,[13] returns the value In Java and C#, not permitted if function is value-returning |
BASIC |
RETURN
|
|
Lisp |
(return value) |
Last statement value |
Perl, Ruby |
return @values; return $value; return; or a contextual return sequence |
Last statement value |
PL/I |
return(expression); return; |
Undefined behavior if procedure is declared as returning a value |
Python |
return value |
None [4]
|
Smalltalk |
^ value |
|
Tcl |
return return $value return -code error "Error message" or some more complicated combination of options |
Last statement value |
Visual Basic .NET |
Return value |
|
Windows PowerShell |
return value; |
Object |
x86 assembly |
ret
|
Contents of eax register (by conventions) |
In some assembly languages, for example that for the MOS Technology 6502, the mnemonic «RTS» (ReTurn from Subroutine) is used.
Multiple return statements [edit]
Languages with an explicit return statement create the possibility of multiple return statements in the same function. Whether or not that is a good thing is controversial.
Strong adherents of structured programming make sure each function has a single entry and a single exit (SESE). It has thus been argued[14] that one should eschew the use of the explicit return statement except at the textual end of a subroutine, considering that, when it is used to «return early», it may suffer from the same sort of problems that arise for the GOTO statement. Conversely, it can be argued that using the return statement is worthwhile when the alternative is more convoluted code, such as deeper nesting, harming readability.
In his 2004 textbook, David Watt writes that «single-entry multi-exit control flows are often desirable». Using Tennent’s framework notion of sequencer, Watt uniformly describes the control flow constructs found in contemporary programming languages and attempts to explain why certain types of sequencers are preferable to others in the context of multi-exit control flows. Watt writes that unrestricted gotos (jump sequencers) are bad because the destination of the jump is not self-explanatory to the reader of a program until the reader finds and examines the actual label or address that is the target of the jump. In contrast, Watt argues that the conceptual intent of a return sequencer is clear from its own context, without having to examine its destination. Furthermore, Watt writes that a class of sequencers known as escape sequencers, defined as «sequencer that terminates execution of a textually enclosing command or procedure», encompasses both breaks from loops (including multi-level breaks) and return statements. Watt also notes that while jump sequencers (gotos) have been somewhat restricted in languages like C, where the target must be an inside the local block or an encompassing outer block, that restriction alone is not sufficient to make the intent of gotos in C self-describing and so they can still produce «spaghetti code». Watt also examines how exception sequencers differ from escape and jump sequencers; for details on this see the article on structured programming.[15]
According to empirical studies cited by Eric S. Roberts, student programmers had difficulty formulating correct solutions for several simple problems in a language like Pascal, which does not allow multiple exit points. For the problem of writing a function to linearly searching an element in an array, a 1980 study by Henry Shapiro (cited by Roberts) found that using only the Pascal-provided control structures, the correct solution was given by only 20% of the subjects, while no subject wrote incorrect code for this problem if allowed to write a return from the middle of a loop.[16]
Others, including Kent Beck and Martin Fowler argue that one or more guard clauses—conditional «early exit» return statements near the beginning of a function—often make a function easier to read than the alternative.[17][18][19][20]
The most common problem in early exit is that cleanup or final statements are not executed – for example, allocated memory is not unallocated, or open files are not closed, causing leaks. These must be done at each return site, which is brittle and can easily result in bugs. For instance, in later development, a return statement could be overlooked by a developer, and an action which should be performed at the end of a subroutine (e.g. a trace statement) might not be performed in all cases. Languages without a return statement, such as standard Pascal don’t have this problem. Some languages, such as C++ and Python, employ concepts which allow actions to be performed automatically upon return (or exception throw) which mitigates some of these issues – these are often known as «try/finally» or similar. Functionality like these «finally» clauses can be implemented by a goto to the single return point of the subroutine. An alternative solution is to use the normal stack unwinding (variable deallocation) at function exit to unallocate resources, such as via destructors on local variables, or similar mechanisms such as Python’s «with» statement.
Some early implementations of languages such as the original Pascal and C restricted the types that can be returned by a function (e.g. not supporting record or struct types) to simplify their compilers.
In Java—and similar languages modeled after it, like JavaScript—it is possible to execute code even after return statement, because the finally block of a try-catch structure is always executed. So if the return statement is placed somewhere within try or catch blocks the code within finally (if added) will be executed. It is even possible to alter the return value of a non primitive type (a property of an already returned object) because the exit occurs afterwards as well.[21]
Yield statements[edit]
Cousin to return statements are yield statements: where a return causes a subroutine to terminate, a yield causes a coroutine to suspend. The coroutine will later continue from where it suspended if it is called again. Coroutines are significantly more involved to implement than subroutines, and thus yield statements are less common than return statements, but they are found in a number of languages.
Call/return sequences[edit]
A number of possible call/return sequences are possible depending on the hardware instruction set, including the following:
- The
CALL
instruction pushes address of the next instruction on the stack and branches to the specified address. TheRETURN
instruction pops the return address from the stack into the instruction pointer and execution resumes at that address. (Examples: x86, PDP-11) - The
CALL
instruction places address of the next instruction in a register and branches to the specified address. TheRETURN
instruction sequence places the return address from the register into the instruction pointer and execution resumes at that address. (Examples: IBM System/360 and successors through z/Architecture, most RISC architectures) - The
CALL
instruction places address of the next (or current) instruction in the storage location at the call address and branches to the specified address+1. TheRETURN
instruction sequence branches to the return address by an indirect jump to the first instruction of the subroutine. (Examples: IBM 1130, SDS 9XX, PDP-8)
See also[edit]
- Return type
- Exit status
Notes[edit]
- ^ in the Bourne shell, only integers in the range 0-255 may be returned[11]
References[edit]
- ^ a b «return Statement (C)». Microsoft Docs. 25 January 2023.
- ^ a b «return Statement (C++)». Microsoft Docs. 3 August 2021.
- ^ Gleaves, R. (2012). Modula-2 for Pascal Programmers. Springer. p. 71. ISBN 9781461385318.
- ^ a b c Martelli, Alex (2006). Python in a Nutshell: A Desktop Quick Reference (2nd ed.). O’Reilly Media. p. 73. ISBN 9781449379100.
- ^ a b Scott, Michael L. (2006). Programming Language Pragmatics. Morgan Kaufmann. p. 432. ISBN 9780126339512.
- ^ Flanders, Harley (2012). Scientific Pascal. Springer. p. 35. ISBN 9781461224280.
- ^ ANSI x3.9-1966. USA Standard FORTRAN (PDF). American National Standards Institute. p. 14. Archived from the original (PDF) on May 15, 2011. Retrieved May 5, 2010.
{{cite book}}
: CS1 maint: numeric names: authors list (link) - ^ ANSI x3.9-1978. American National Standard – Programming Language FORTRAN. American National Standards Institute. 15.8 RETURN Statement. Archived from the original on October 29, 2013. Retrieved December 11, 2007.
{{cite book}}
: CS1 maint: numeric names: authors list (link) - ^ Sakkinen, Markku (March 1989). «How to best return the value of a function». ACM SIGPLAN Notices. Association for Computing Machinery. 24 (3): 55–56. doi:10.1145/66083.66087.
- ^ Wirth, Niklaus (May 3, 2016). «10. Procedure declarations». The Programming Language Oberon (PDF) (Report). p. 11.
- ^ «return — return from a function or dot script». Single UNIX Specification.
- ^ «PHP: return — Manual». PHP Manual. The PHP Group. Retrieved 26 March 2013.
- ^ «Return — Javascript». MDN Javascript Reference. Mozilla Developer Network. Retrieved 27 March 2013.
- ^ «C++ Notes: Function return Statement».
- ^ Watt, David Anthony; Findlay, William (2004). Programming Language Design Concepts. John Wiley & Sons. pp. 215–221. ISBN 978-0-470-85320-7.
- ^ Roberts, E. (March 1995). «Loop Exits and Structured Programming: Reopening the Debate». ACM SIGCSE Bulletin. 27 (1): 268–272. doi:10.1145/199691.199815.
- ^ Martin Fowler; Kent Beck; John Brant; William Opdyke; Don Roberts (2012). Refactoring: Improving the Design of Existing Code (Google eBook). Addison-Wesley. pp. 237, 250. ISBN 9780133065268.
… one exit point mentality … I don’t follow the rule about one exit point from a method.
- ^
Kent Beck (2007). «7: Behavior». Implementation Patterns. Pearson Education. section «Guard Clause». ISBN 9780132702553. - ^ «Multiple return statements». Java Practices.
- ^ Fred Swartz. «Return statements and the single exit fantasy». Archived from the original on 2020-02-23.
- ^ «The finally Block». The Java Tutorials.
- Home
- C Programming Tutorial
- The return statement in C
Last updated on July 27, 2020
The return statement is used to return some value or simply pass the control to the calling function. The return statement can be used in the following two ways.
return;
return expression;
The first form of the return statement is used to terminate the function and pass the control to the calling function. No value from the called function is returned when this form of the return
statement is used.
The following program demonstrates the use of the first form of the return
statement.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include<stdio.h> void eligible_or_not(int x); int main() { int n; printf("Enter age: "); scanf("%d", &n); eligible_or_not(n); // signal to operating system everything works fine return 0; } void eligible_or_not(int age) { if(age >= 18) { printf("You are eligible to vote"); return; } else if(age == 17 ) { printf("Try next year"); return; } else { printf("You are not yet ready"); return; } } |
Expected Output:
1st run:
Enter age: 16 You are not yet ready |
2nd run:
Enter age: 17 Try next year |
3rd run:
Enter age: 19 You are eligible to vote |
How it works
Let’s say the user entered 17
(value of variable n
), then eligible_or_not()
function is called, this function expects one argument of type int
, which we have correctly supplied as n
. The value of variable n is assigned to variable age
. Remember age
is a local variable, and thus only available inside eligible_or_not()
function. if condition (age >= 18)
is tested, since it is false, the statement under if block is omitted. Control passes to else statement and condition (age == 17)
is tested, since it is true, statements under else if block is executed. The return
statement under the else if block passes the control back to calling function i.e main()
. If the return
statement would not have been there in the else if block, the control would have been moved ahead to execute the statement following if-else statement.
The second form of the return statement is used to return values from a function. The expression following return can be any constant, variable, function call etc.
The following program computes the factorial of a number using a function.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
#include<stdio.h> int factorial(int x); // declaration of factorial function int main() { int n; printf("Calculate factorial: \n\n"); printf("Enter number : "); scanf("%d", &n); if(n < 0) { printf("\nFactorial is only defined for positive numbers"); } else { printf("\n%d! = %d", n, factorial(n)); // calling factorial function } // signal to operating system everything works fine return 0; } // definition of factorial function int factorial(int n) { if(n == 0) { return 1; } int f = 1, i; for(i = n; i > 0; i-- ) { f = f * i; } return f; } |
Expected Output:
Calculate factorial: Enter number : 5 5! = 120 |
How it works
There is nothing extraordinary happening in the main() function, so we are only going to explain how factorial()
function works. Let’s say the user entered 5, in line 18 the factorial()
function is called, along with an argument of type int. In line 30, if condition (n==0)
is checked, since it is false, statements inside the if block are omitted. If the condition (n==0)
would have been true, then the statement return 1;
(Why we are returning 1
, because factorial of 0
is 1
) would have been executed, and the control would have been transferred back to main()
function, effectively terminating the factorial()
function.
In line 34, two variables f
and i
of type int are declared and the variable f
is assigned a value of 1
.
In line 36, for loop’s initialization expression is executed, i
is assigned the value of n
. Then the condition (i>0)
is tested, since it is true, statement inside the for body is executed. At this point, the value of f
is 5
. This completes the first iteration. Then update expression i--
is executed. This process continues until i
is greater than 0
. The following table shows the value of i
and fact
after each iteration.
Iteration | Value of i | Value of fact |
---|---|---|
1 |
5 |
5 |
2 |
4 |
5*4 = 20 |
3 |
3 |
20* 3 = 60 |
When i
reaches 0
, the condition (i > 0)
becomes false and the control breaks out of the for loop to execute statements following the loop.
In line 40, the return statement causes the factorial()
function to terminate and return the value of variable f
to the calling function main()
.
На этом занятии подробнее ознакомимся с работой оператора return.
Давайте
предположим, что нам нужно объявить функцию, которая бы вычисляла корень четвертой
степени от переданного значения x. Ее можно записать и вызвать следующим
образом:
#include <stdio.h> #include <math.h> double sq4_x(double x) { double res = (x < 0) ? NAN : pow(x, 0.25); return res; } int main(void) { printf("%f\n", sq4_x(16)); return 0; }
Функция sq4_x() имеет
возвращаемый тип double и один параметр этого же типа. Затем,
во временную переменную res заносится
значение nan (сокращение от Not a Number – не число)
если переменная x меньше нуля, а иначе корень четвертой
степени. Далее, записан оператор return с указанием возвращаемого
значения – переменной res. Ниже, в функции main(), вызывается
функция sq4_x() с аргументом
16 и значение выводится на экран. После запуска программы увидим результат:
2.000000
Очевидно, это
число 2.0 было возвращено функцией sq4_x() благодаря
оператору return. Если этот
оператор убрать, то функция будет возвращать неопределенные значения. Однако в современных
стандартах языка Си/С++ оператор return строго
обязателен, если тип функции отличен от void. Поэтому
следует придерживаться правила: если функция возвращает какой-либо определенный
тип данных (не void), то в ней следует прописывать оператор return.
В
действительности, этот оператор делает две вещи: собственно, возвращает
указанные значения; и завершает выполнение функции. Вот этот второй момент не
следует упускать из виду. Например, если после оператора return прописать еще
какой-либо оператор, например:
double sq4_x(double x) { double res = (x < 0) ? NAN : pow(x, 0.25); return res; puts("sq4_x"); }
то он выполнен
не будет, так как выполнение функции завершится на операторе return.
Это свойство
часто используют для досрочного завершения функций. Например, в нашем примере,
мы это могли бы использовать так:
double sq4_x(double x) { if(x < 0) return NAN; return pow(x, 0.25); }
Вначале
проверяем, если x меньше нуля, то сразу завершаем
выполнение функции с возвращением специального значения nan. Иначе, при
ложности условия, выполнение тела функции дойдет до второго оператора return и будет
вычислен корень четвертой степени.
Оператор return в функции типа void
Если при
объявлении функции возвращаемый тип указан как void, то оператор return в ней прописывать
не обязательно. Например, функция, которая выводит на экран значение переданной
переменной, если она четная:
void print_even_x(int x) { if(x % 2 == 0) printf("x = %d\n", x); }
Здесь нет
никакого оператора return. Функция просто выводит значение для
четных чисел. Ниже в функции main() мы можем ее вызвать следующим
образом:
int main(void) { printf("%f\n", sq4_x(-16)); print_even_x(4); return 0; }
Программа
скомпилируется и успешно запустится. Однако эту же функцию print_even_x() можно
записать и с оператором return, например, так:
void print_even_x(int x) { if(x % 2 != 0) return; printf("x = %d\n", x); }
Он будет
срабатывать при нечетных x и, соответственно, досрочно завершать
выполнение функции. Обратите внимание, после return сразу ставится
точка с запятой без указания возвращаемого значения. Когда функция имеет тип void, она ничего не
возвращает, а значит, после оператора return не нужно ничего
прописывать.
Функции, как отдельные, независимые модули программы
Вообще,
правильно раскладывать программу на наборы функций – это, в некотором смысле,
искусство – искусство программирования. На первых порах начинающие кодеры суют
в функции все подряд. В результате, они получаются громоздкими, корявыми,
неудобными для дальнейшего применения. Чтобы избежать такой участи, следует на
начальных этапах придерживаться нескольких простых правил:
-
функции
следует выполнять только свою узко поставленную задачу; -
для
решения поставленной задачи, функции следует использовать только переданные ей
параметры; -
если
тело функции становится слишком большим – это повод задуматься над
правильностью организации логики программы; возможно, подзадачу следует
раздробить на более мелкие подзадачи; -
в
99% случаях функции ничего не считывают с клавиатуры (из потока stdin) и ничего не
выводят на экран (в поток stdout); если ваша функция делает это,
убедитесь, что это действительно необходимо.
Вызов функций в аргументах
Давайте я покажу
на конкретном примере красоту решения задачи с использованием функций. Предположим,
перед нами ставится задача объявления функций для поиска минимального числового
значения из двух и из трех возможных. Вначале определим функцию для нахождения
минимального среди двух значений, например, так:
double min_2(double a, double b) { return (a < b) ? a : b; }
А, затем, на
основе этой функции, определим вторую – для нахождения минимального среди трех
чисел:
double min_3(double a, double b, double c) { double min_ab = min_2(a, b); return (min_ab < c) ? min_ab : c; }
Видите, как
красиво можно подойти к решению этой задачи, если действовать не в лоб, а
разбивать задачу на более простые, а потом, объединять их, получая конечный,
более сложный результат.
Давайте теперь
посмотрим, как можно использовать эти функции:
int main(void) { int x = 1, y = -2, z = 10; double res_1 = min_2(x, y); double res_2 = min_3(x, y, z); double res_3 = min_2(min_2(x, y), z); double res_4 = min_2(x, min_2(y, z)); printf("res_1 = %.2f, res_2 = %.2f, res_3 = %.2f, res_4 = %.2f\n", res_1, res_2, res_3, res_4); return 0; }
Первые два
вызова очевидны, мы просто передаем значения и получаем минимальное из них. А
во вторых двух вызовах в качестве аргумента записан еще один вызов функции min_2(). Так тоже
вполне можно делать. В результате, сначала отработает функция min_2(x, y), будет
получено минимальное среди этих двух значений, а затем, оно передается как
аргумент первой функции min_2(). В итоге, с помощью min_2() мы находим
минимальное для трех чисел. По аналогии отрабатывает и следующая строчка
программы.
Как вы
понимаете, мы можем пойти еще дальше и использовать функции min_2() и min_3() следующим образом:
double r4 = min_2(min_2(-2, 3), min_2(x, y)); double r5 = min_2(min_2(-2, 3), min_3(x, y, z));
В итоге получим
минимальное среди четырех и пяти чисел. Видите, какое многообразие решений дают
эти две функции, при условии их правильного проектирования. Но здесь возникает
вопрос, в каком порядке будут вызваны функции, записанные в аргументах? Например,
при нахождении минимального среди пяти чисел, что будет вызвано вначале min_2() или min_3()? В
действительности, стандартом языка Си этот момент не определен. Поэтому разные
компиляторы могут транслировать программу так, что сначала min_3() вызовется,
а затем, min_2(), или
наоборот. Гарантии никакой нет. Однако точно можно сказать, что вначале отрабатывают
функции, стоящие в аргументах, и только после этого основная функция. В этом мы
можем быть уверены.
На первый взгляд
может показаться, что нет особой разницы, в каком порядке срабатывают функции в
аргументах. Но это не всегда так. Пусть, например, мы задумали использовать
операцию инкремента при вызове функции min_2():
double res_1 = min_2(++x, x); printf("res_1 = %.2f\n", res_1);
И думаем, ага,
вначале отработает первый аргумент и переменная x увеличится на 1,
а затем, второй. В результате, минимальное будет равно 2. Но после запуска
программы видим другое значение 1. А вот если в моем случае записать инкремент
у второго аргумента:
double res_1 = min_2(x, ++x);
то получаем
значение 2. То есть, порядок иногда имеет значение, но, так как мы его не
знаем, то полагаться на подобные конструкции не стоит. Можно использовать
инкремент или декремент, только если такая же переменная не стоит в других
аргументах, например, так:
double res_1 = min_2(y, ++x);
В этом случае
порядок для нас не важен. И, обратите внимание, операция инкремента у
переменной x записана в
префиксной форме. Почему именно так? Очевидно, в этом случае значение второго
параметра b функции min_2() будет
принимать значение:
То есть,
значение x будет увеличено
на 1 и только после этого присвоено переменной b. Если же при
вызове функции min_2() использовать постфиксную форму записи:
double res_1 = min_2(y, x++);
то это будет
эквивалентно присваиванию:
Соответственно,
значение параметра b будет равно прежнему значению x без увеличения
на единицу. Вот этот момент нужно хорошо себе представлять.
Видео по теме
Pre-requisite: Functions in C
C return statement ends the execution of a function and returns the control to the function from where it was called. The return statement may or may not return a value depending upon the return type of the function. For example, int returns an integer value, void returns nothing, etc.
In C, we can only return a single value from the function using the return statement and we have to declare the data_type of the return value in the function definition/declaration.
Syntax:
return return_value;
Working of Return Statement
There are various ways to use return statements. A few are mentioned below:
1. Methods not returning a value
In C, one cannot skip the return statement when the return type of the function is non-void type. The return statement can be skipped only for void types.
A. Not using a return statement in void return type function:
While using the void function, it is not necessary to use return as the void itself means nothing(an empty value).
Syntax:
void func() { . . . }
Example:
C
#include <stdio.h>
void
Print()
{
printf
(
"Welcome to GeeksforGeeks"
);
}
int
main()
{
Print();
return
0;
}
Output:
Welcome to GeeksforGeeks
B. Using the return statement in the void return type function
As void means empty, we don’t need to return anything, but we can use the return statement inside void functions as shown below. Although, we still cannot return any value.
Syntax:
void func() { return; }
This syntax is used in function as a jump statement in order to break the flow of the function and jump out of it. One can think of it as an alternative to “break statement” to use in functions.
Example:
C
#include <stdio.h>
void
Print()
{
printf
(
"Welcome to GeeksforGeeks"
);
return
;
}
int
main()
{
Print();
return
0;
}
Output:
Welcome to GeeksforGeeks
But if the return statement tries to return a value in a void return type function, that will lead to errors.
Incorrect Syntax:
void func() { return value; }
Example:
C
#include <stdio.h>
void
Print()
{
printf
(
"Welcome to GeeksforGeeks"
);
return
10;
}
int
main()
{
Print();
return
0;
}
Warnings:
prog.c: In function 'Print': prog.c:12:9: warning: 'return' with a value, in function returning void return 10; ^
2. Methods returning a value
For functions that define a non-void return type in the definition and declaration, the return statement must be immediately followed by the return value of that specified return type.
Syntax:
return-type func() { return value; }
Example:
C
#include <stdio.h>
int
SUM(
int
a,
int
b)
{
int
s1 = a + b;
return
s1;
}
int
main()
{
int
num1 = 10;
int
num2 = 10;
int
sum_of = SUM(num1, num2);
printf
(
"The sum is %d"
, sum_of);
return
0;
}
Note: A function can only return a single value using return statement. To return multiple values, we use pointers or structures. Know more about refer to the article – How to return multiple values from a function in C or C++?.
Whether you’re preparing for your first job interview or aiming to upskill in this ever-evolving tech landscape, GeeksforGeeks Courses are your key to success. We provide top-quality content at affordable prices, all geared towards accelerating your growth in a time-bound manner. Join the millions we’ve already empowered, and we’re here to do the same for you. Don’t miss out — check it out now!
Last Updated :
02 Jan, 2023
Like Article
Save Article