Из Википедии, бесплатной энциклопедии
Перейти к навигации Перейти к поиску

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

Обзор [ править ]

В C и C ++ , (где это выражение ) является утверждение , что говорит функцию для выполнения возврата программы вызывающей функции, и сообщить значение . Если функция имеет тип возврата void , оператор return может использоваться без значения, и в этом случае программа просто вырывается из текущей функции и возвращается к вызывающей.return exp;expexp

В Паскале нет оператора возврата. (Однако в более новых Паскалях можно использовать для немедленного возврата значения. Без параметров он просто прерывает процедуру.) Подпрограмма автоматически возвращается, когда выполнение достигает своего последнего исполняемого оператора. Значения могут быть возвращены путем присвоения идентификатору, имеющему то же имя, что и подпрограмма, функции в терминологии Паскаля. Таким образом, идентификатор функции используется для рекурсивных вызовов и в качестве держателя результата; синтаксически это похоже на явный выходной параметр . Тот же синтаксис используется в Fortran 66 и Fortran 77, хотя оператор return был добавлен в FORTRAN II.Exit(exp);. В некоторых других языках вместо идентификатора функции используется определяемая пользователем переменная результата.

Оберон ( Оберон-07 ) имеет предложение возврата вместо оператора возврата. Предложение return помещается после последнего оператора тела процедуры. Это позволяет во время компиляции проверять правильность возврата и возвращаемого значения из процедуры.

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

В других случаях возвращается значение Null, если нет явного оператора возврата: в Python значение Noneвозвращается, когда оператор возврата опущен, тогда как в JavaScript undefinedвозвращается значение .

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

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

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

Синтаксис [ править ]

Заявления возврата бывают разных форм. Наиболее распространены следующие синтаксисы:

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

Множественные операторы возврата [ править ]

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

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

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

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

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

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

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

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

Отчетность о доходности [ править ]

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

Последовательности звонков / ответов [ править ]

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

  1. CALLИнструкция выталкивает адрес следующей инструкции на стеке и филиалов по указанному адресу. RETURNИнструкция выталкивает адрес возврата из стека в указатель команд и выполнение программы продолжается по указанному адресу. (Примеры x86, PDP-11)
  2. В CALLместах команд - адрес следующей команды в регистре и филиалов по указанному адресу. Последовательность RETURNкоманд помещает адрес возврата из регистра в указатель команд, и выполнение возобновляется с этого адреса. (Пример IBM System / 360)
  3. В CALLместах инструкции адрес следующего (или тока ) инструкции в месте хранения по адресу вызова и филиалы по указанному адресу + 1. Последовательность RETURNинструкций переходит к адресу возврата путем косвенного перехода к первой инструкции подпрограммы. (Примеры IBM 1130, SDS9XX)

См. Также [ править ]

  • Тип возврата
  • Статус выхода

Ссылки [ править ]

  1. ^ в оболочке Bourne могут быть возвращены только целые числа в диапазоне 0-255: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#return
  2. ^ https://msdn.microsoft.com/en-us/library/sta56yeb.aspx MSDN: инструкция возврата (C)
  3. ^ https://msdn.microsoft.com/en-us/library/k68ktdwf.aspx MSDN: инструкция возврата (C ++)
  4. ^ «PHP: возврат - Руководство» . Руководство по PHP . Группа PHP . Проверено 26 марта 2013 года .
  5. ^ «Возврат - Javascript» . Справочник по MDN Javascript . Сеть разработчиков Mozilla . Проверено 27 марта 2013 года .
  6. ^ C ++ Примечания: Заявление о возврате функции
  7. ^ Дэвид Энтони Уотт; Уильям Финдли (2004). Концепции проектирования языков программирования . Джон Вили и сыновья. С. 215–221. ISBN 978-0-470-85320-7.
  8. ^ Робертс, Э. [1995] «Выходы из цикла и структурированное программирование: возобновление дискуссии», Бюллетень ACM SIGCSE, (27) 1: 268–272.
  9. ^ Мартин Фаулер, Кент Бек, Джон Брант, Уильям Опдайк, Дон Робертс. «Рефакторинг: улучшение дизайна существующего кода (электронная книга Google)» . раздел «Заменить вложенные условия на защитные предложения». 2012. с. 237, стр. 250. Цитата: «... менталитет одной точки выхода ... Я не следую правилу об одной точке выхода из метода».
  10. ^ Кент Бек. «Шаблоны реализации» . 2007. «Глава 7: Поведение», раздел «Предохранительная оговорка».
  11. ^ "Множественные операторы возврата"
  12. ^ Фред Шварц. «Заявления о возврате и фантазия о единственном выходе» .
  13. ^ Последний блок, Учебники по Java