stringtranslate.com

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

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

Обзор

В C и C++ ( где — выражение ) — это оператор , который сообщает функции о необходимости вернуть выполнение программы в вызывающую функцию и сообщить значение . Если функция имеет возвращаемый тип void , оператор return можно использовать без значения, в этом случае программа просто выходит из текущей функции и возвращается в вызывающую. [1] [2] Похожий синтаксис используется в других языках, включая Modula-2 [3] и Python . [4]return exp;expexp

В Pascal нет оператора возврата. Функции или процедуры автоматически возвращаются при достижении своего последнего оператора. Возвращаемое значение из функции предоставляется внутри функции путем присвоения идентификатору с тем же именем, что и у функции. [5] Однако некоторые версии Pascal предоставляют специальную функцию , которая может использоваться для немедленного возврата значения из функции или, без параметров, для немедленного возврата из процедуры. [6]Exit(exp);

Подобно Pascal, FORTRAN II , Fortran 66 , Fortran 77 и более поздние версии Fortran определяют возвращаемые значения путем присвоения имени функции, но также имеют оператор return; этот оператор не определяет возвращаемое значение и, для функции, приводит к возврату значения, назначенного имени функции. [5] [7] [8]

В некоторых других языках вместо идентификатора функции используется выходной параметр, определяемый пользователем . [9]

Oberon ( Oberon-07 ) имеет предложение return вместо оператора return. Предложение return размещается после последнего оператора тела процедуры. [10]

Некоторые языки программирования, ориентированные на выражения , такие как Lisp , Perl и Ruby , позволяют программисту опускать явный оператор return, указывая вместо этого, что последнее вычисленное выражение является возвращаемым значением подпрограммы. В других случаях возвращается значение Null, если явный оператор return отсутствует: в Python значение Noneвозвращается, когда оператор return опущен, [4]undefined тогда как в JavaScript возвращается значение .

В Windows PowerShell все вычисляемые выражения, которые не были захвачены (например, назначены переменной, приведены к void или переданы по конвейеру в $null ), возвращаются из подпрограммы как элементы массива или как один объект в случае, если не был захвачен только один объект.

В Perl возвращаемое значение или значения подпрограммы могут зависеть от контекста, в котором она была вызвана. Наиболее фундаментальным различием является скалярный контекст, в котором вызывающий код ожидает одно значение, списочный контекст, в котором вызывающий код ожидает список значений, и пустой контекст, в котором вызывающий код вообще не ожидает никакого возвращаемого значения. Подпрограмма может проверять контекст с помощью wantarrayфункции. Специальный синтаксис return without arguments используется для возврата неопределенного значения в скалярном контексте и пустого списка в списочном контексте. Скалярный контекст можно далее разделить на контексты Boolean , number , string и различные ссылочные типы. Кроме того, контекстно-зависимый объект может быть возвращен с помощью контекстной последовательности возврата с ленивой оценкой скалярных значений.

Многие операционные системы позволяют программе возвращать результат (отдельно от обычного вывода ) при завершении ее процесса; эти значения называются статусами выхода . Количество информации, которая может быть передана таким образом, довольно ограничено, на практике часто ограничивается сигнализацией успеха или неудачи. Изнутри программы этот возврат обычно достигается вызовом Exit (системный вызов) (распространен даже в C, где доступен альтернативный механизм возврата из основной функции ).

Синтаксис

Операторы возврата могут иметь множество форм. Наиболее распространены следующие синтаксисы:

В некоторых языках ассемблера , например, в языке MOS Technology 6502 , используется мнемоника «RTS» (ReTurn from Subroutine).

Несколько заявлений о возврате

Языки с явным оператором return создают возможность множественных операторов return в одной и той же функции. Хорошо это или нет — вопрос спорный.

Ярые приверженцы структурного программирования гарантируют, что каждая функция имеет один вход и один выход (SESE). Таким образом, утверждается [14] , что следует избегать использования явного оператора return, за исключением текстового конца подпрограммы, учитывая, что при его использовании для «раннего возврата» он может страдать от тех же проблем, которые возникают для оператора GOTO . И наоборот, можно утверждать, что использование оператора return имеет смысл, когда альтернативой является более запутанный код, такой как более глубокая вложенность, что вредит читабельности.

В своем учебнике 2004 года Дэвид Уотт пишет, что «однозаходные многовыходные потоки управления часто желательны». Используя фреймворковое понятие секвенсора Теннента , Уотт единообразно описывает конструкции потока управления, встречающиеся в современных языках программирования, и пытается объяснить, почему определенные типы секвенсоров предпочтительнее других в контексте многовыходных потоков управления. Уотт пишет, что неограниченные goto (секвенсоры перехода) плохи, потому что место назначения перехода не является самоочевидным для читателя программы, пока читатель не найдет и не изучит фактическую метку или адрес, который является целью перехода. Напротив, Уотт утверждает, что концептуальное намерение секвенсора возврата ясно из его собственного контекста, без необходимости исследовать его место назначения. Кроме того, Уотт пишет, что класс секвенсоров, известных как escape-секвенсоры , определяемый как «секвенсор, который завершает выполнение текстуально охватывающей команды или процедуры», охватывает как разрывы циклов (включая многоуровневые разрывы), так и операторы возврата. Уотт также отмечает, что хотя в таких языках, как C, секвенсоры переходов (goto) были несколько ограничены, где цель должна быть внутри локального блока или охватывающего внешнего блока, одного этого ограничения недостаточно, чтобы сделать намерение goto в C самоописываемым, и поэтому они все еще могут производить « спагетти-код ». Уотт также исследует, чем секвенсоры исключений отличаются от секвенсоров выхода и перехода; подробности об этом см. в статье о структурном программировании. [15]

Согласно эмпирическим исследованиям, цитируемым Эриком С. Робертсом , студенты-программисты испытывали трудности с формулированием правильных решений для нескольких простых задач на таком языке, как Pascal, который не допускает множественных точек выхода. Для задачи написания функции для линейного поиска элемента в массиве исследование Генри Шапиро 1980 года (цитируемое Робертсом) показало, что при использовании только управляющих структур, предоставляемых Pascal, правильное решение было дано только 20% испытуемых, в то время как ни один испытуемый не написал неправильный код для этой задачи, если ему было разрешено написать возврат из середины цикла. [16]

Другие, включая Кента Бека и Мартина Фаулера, утверждают, что одно или несколько защитных предложений — условных операторов возврата «раннего выхода» вблизи начала функции — часто делают функцию более простой для чтения, чем альтернативные варианты. [17] [18] [19] [20]

Наиболее распространенной проблемой при раннем выходе является то, что не выполняются очистка или финальные операторы — например, выделенная память не освобождается или открытые файлы не закрываются, что приводит к утечкам. Это должно быть сделано в каждом месте возврата, что является хрупким и может легко привести к ошибкам. Например, при более поздней разработке оператор возврата может быть упущен разработчиком, и действие, которое должно быть выполнено в конце подпрограммы (например, оператор трассировки ), может быть выполнено не во всех случаях. Языки без оператора возврата, такие как стандартный Pascal, не имеют этой проблемы. Некоторые языки, такие как C++ и Python, используют концепции, которые позволяют автоматически выполнять действия при возврате (или выдаче исключения), что смягчает некоторые из этих проблем — они часто известны как «try/finally» или аналогичные. Функциональность, подобная этим предложениям «finally», может быть реализована с помощью goto к единственной точке возврата подпрограммы. Альтернативным решением является использование обычной раскрутки стека (освобождения переменных) при выходе из функции для освобождения ресурсов, например, с помощью деструкторов локальных переменных или аналогичных механизмов, таких как оператор «with» в Python.

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

В Java — и подобных языках, созданных по его образцу, таких как JavaScript — возможно выполнение кода даже после оператора return, поскольку блок finally структуры try-catch выполняется всегда. Таким образом, если оператор return помещен где-то внутри блоков try или catch, код внутри finally (если он добавлен) будет выполнен. Возможно даже изменить возвращаемое значение не примитивного типа (свойство уже возвращенного объекта), поскольку выход также происходит после этого. [21]

Отчеты о доходности

Двоюродными братьями операторов return являются операторы yield : где return вызывает завершение подпрограммы , yield приводит к приостановке сопрограммы . Сопрограмма позже продолжится с того места, где она была приостановлена, если она будет вызвана снова. Сопрограммы значительно сложнее в реализации, чем подпрограммы, и поэтому операторы yield встречаются реже, чем операторы return, но они встречаются в ряде языков.

Последовательности вызова/возврата

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

  1. Инструкция CALLпомещает адрес следующей инструкции в стек и переходит на указанный адрес. RETURNИнструкция помещает адрес возврата из стека в указатель инструкций, и выполнение возобновляется с этого адреса. (Примеры: x86 , PDP-11 ) В таких архитектурах, как Motorola 96000 , область стека может быть выделена в отдельном адресном пространстве, которое называется «пространством стековой памяти» [22] , отличным от адресного пространства основной памяти. [23] NEC μPD7720 также имеет стек со своим собственным отдельным адресным пространством. [24]
  2. Инструкция CALLпомещает адрес следующей инструкции в регистр и переходит по указанному адресу. RETURNПоследовательность инструкций помещает адрес возврата из регистра в указатель инструкций, и выполнение возобновляется с этого адреса. (Примеры: IBM System/360 и последующие версии через z/Architecture , большинство архитектур RISC )
  3. Инструкция CALLпомещает адрес следующей (или текущей ) инструкции в ячейку памяти по адресу вызова и переходит на указанный адрес+1. RETURNПоследовательность инструкций переходит на адрес возврата с помощью косвенного перехода к первой инструкции подпрограммы. (Примеры: IBM 1130 , SDS 9XX , PDP-8 )

Смотрите также

Примечания

  1. ^ в оболочке Bourne могут быть возвращены только целые числа в диапазоне 0-255 [11]

Ссылки

  1. ^ ab "return Statement (C)". Microsoft Docs . 25 января 2023 г.
  2. ^ ab "return Statement (C++)". Microsoft Docs . 3 августа 2021 г.
  3. ^ Gleaves, R. (2012). Modula-2 для программистов на Pascal. Springer. стр. 71. ISBN 9781461385318.
  4. ^ abc Мартелли, Алекс (2006). Python in a Nutshell: A Desktop Quick Reference (2-е изд.). O'Reilly Media. стр. 73. ISBN 9781449379100.
  5. ^ ab Скотт, Майкл Л. (2006). Прагматика языка программирования. Морган Кауфманн. стр. 432. ISBN 9780126339512.
  6. ^ Фландерс, Харли (2012). Scientific Pascal. Springer. стр. 35. ISBN 9781461224280.
  7. ^ ANSI x3.9-1966. Стандарт США FORTRAN (PDF) . Американский национальный институт стандартов. стр. 14. Архивировано из оригинала (PDF) 15 мая 2011 г. . Получено 5 мая 2010 г. .{{cite book}}: CS1 maint: числовые имена: список авторов ( ссылка )
  8. ^ ANSI x3.9-1978. Американский национальный стандарт – Язык программирования FORTRAN. Американский национальный институт стандартов. 15.8 Заявление RETURN. Архивировано из оригинала 29 октября 2013 г. Получено 11 декабря 2007 г.{{cite book}}: CS1 maint: числовые имена: список авторов ( ссылка )
  9. ^ Саккинен, Маркку (март 1989). «Как лучше всего вернуть значение функции». ACM SIGPLAN Notices . 24 (3). Association for Computing Machinery: 55–56. doi : 10.1145/66083.66087 .
  10. ^ Вирт, Никлаус (3 мая 2016 г.). "10. Декларации процедур". Язык программирования Oberon (PDF) (Отчет). стр. 11.
  11. ^ "возврат - возврат из функции или точечного скрипта". Единая спецификация UNIX .
  12. ^ "PHP: return - Manual". PHP Manual . PHP Group . Получено 26 марта 2013 г. .
  13. ^ "Возврат - Javascript". Справочник MDN Javascript . Mozilla Developer Network . Получено 27 марта 2013 г.
  14. ^ «Заметки по C++: Оператор возврата функции».
  15. ^ Уотт, Дэвид Энтони; Финдли, Уильям (2004). Концепции проектирования языков программирования . John Wiley & Sons. стр. 215–221. ISBN 978-0-470-85320-7.
  16. ^ Робертс, Э. (март 1995 г.). «Выходы из цикла и структурное программирование: возобновление дебатов». ACM SIGCSE Bulletin . 27 (1): 268–272. doi : 10.1145/199691.199815 .
  17. ^ Мартин Фаулер; Кент Бек; Джон Брант; Уильям Опдайк; Дон Робертс (2012). Рефакторинг: улучшение дизайна существующего кода (электронная книга Google). Addison-Wesley. стр. 237, 250. ISBN 9780133065268. ... менталитет одной точки выхода ... Я не следую правилу о наличии одной точки выхода из метода.
  18. ^ Кент Бек (2007). "7: Поведение". Модели реализации. Pearson Education. раздел "Оговорка о защите". ISBN 9780132702553.
  19. ^ "Множественные операторы возврата". Java Practices .
  20. ^ Фред Шварц. "Return statement and the single exit fantasy". Архивировано из оригинала 23.02.2020.
  21. ^ "The finally Block". Учебники Java .
  22. ^ "DSP96002 32-БИТНЫЙ ЦИФРОВОЙ СИГНАЛЬНЫЙ ПРОЦЕССОР РУКОВОДСТВО ПОЛЬЗОВАТЕЛЯ" (PDF) . стр. 27(3 - ​​4) . Получено 24.12.2023 .
  23. ^ "DSP96002 32-БИТНЫЙ ЦИФРОВОЙ СИГНАЛЬНЫЙ ПРОЦЕССОР РУКОВОДСТВО ПОЛЬЗОВАТЕЛЯ" (PDF) . стр. 50(4 - 11) . Получено 24.12.2023 .
  24. ^ "μPD77C20A, 7720A, 77P20 Цифровой сигнальный процессор". стр. 4(3a-4) . Получено 2023-12-25 .