Оператор возврата
Эта статья нуждается в дополнительных цитатах для проверки . ( февраль 2011 г. ) |
В компьютерном программировании оператор возврата заставляет выполнение покинуть текущую подпрограмму и возобновить ее с той точки кода, которая находится сразу после инструкции, вызвавшей подпрограмму, известную как ее адрес возврата . Адрес возврата сохраняется вызывающей подпрограммой, сегодня обычно в или процесса стеке вызовов в регистре . Операторы возврата во многих языках программирования позволяют функции указывать возвращаемое значение , которое должно быть передано обратно в код , вызвавший функцию.
Обзор [ править ]
В C и C ++ return exp;
(где exp
— выражение ) — это оператор , который сообщает функции, что нужно вернуть выполнение программы вызывающей функции, и сообщить значение exp
. Если функция имеет тип возврата void , оператор return может использоваться без значения, и в этом случае программа просто выходит из текущей функции и возвращается к вызывающей. [1] [2] Похожий синтаксис используется в других языках, включая Modula-2. [3] и Питон . [4]
В Паскале нет оператора возврата. Функции или процедуры автоматически возвращаются при достижении своего последнего оператора. Возвращаемое значение функции предоставляется внутри функции путем присвоения идентификатора с тем же именем, что и у функции. [5] Однако некоторые версии Паскаля предоставляют специальную функцию. Exit(exp);
который можно использовать для немедленного возврата значения из функции или, без параметров, для немедленного возврата из процедуры. [6]
Как и Pascal, FORTRAN II , Fortran 66 , Fortran 77 и более поздние версии Fortran указывают возвращаемые значения путем присвоения имени функции, но также имеют оператор return; этот оператор не указывает возвращаемое значение и для функции вызывает возврат значения, присвоенного имени функции. [5] [7] [8]
определяемый пользователем выходной параметр . В некоторых других языках вместо идентификатора функции используется [9]
Оберон ( Оберон-07 ) имеет предложение возврата вместо оператора возврата. Предложение return помещается после последнего оператора тела процедуры. [10]
Некоторые языки программирования, ориентированные на выражения , такие как Lisp , Perl и Ruby , позволяют программисту опускать явный оператор возврата, указывая вместо этого, что последнее вычисленное выражение является возвращаемым значением подпрограммы. В других случаях возвращается значение Null, если нет явного оператора возврата: в Python значение None
возвращается, если оператор return опущен, [4] в то время как в JavaScript значение undefined
возвращается.
В Windows PowerShell все вычисленные выражения, которые не были захвачены (например, присвоены переменной, приведены к void или переданы по конвейеру в $null ), возвращаются из подпрограммы как элементы массива или как один объект в случае, если только один объект не был захвачен.
В Perl возвращаемое значение или значения подпрограммы могут зависеть от контекста, в котором она была вызвана. Самым фундаментальным различием является скалярный контекст, в котором вызывающий код ожидает одно значение, контекст списка , в котором вызывающий код ожидает список значений, и пустой контекст, в котором вызывающий код вообще не ожидает никакого возвращаемого значения. Подпрограмма может проверить контекст, используя wantarray
функция. Специальный синтаксис возврата без аргументов используется для возврата неопределенного значения в скалярном контексте и пустого списка в списковом контексте. Скалярный контекст можно дополнительно разделить на логический , числовой, строковый и различные контексты ссылочных типов. Кроме того, контекстно-зависимый объект может быть возвращен с использованием контекстной возвращаемой последовательности с ленивой оценкой скалярных значений.
Многие операционные системы позволяют программе возвращать результат (отдельный от обычного вывода ) при завершении ее процесса; эти значения относятся к статусам выхода . Объем информации, которая может быть передана таким образом, весьма ограничен и на практике часто ограничивается сигнализацией об успехе или неудаче. Изнутри программы этот возврат обычно достигается путем вызова Exit (системного вызова) альтернативный механизм возврата из основной функции (распространенного даже в C, где доступен ).
Синтаксис [ править ]
Операторы возврата бывают разных форм. Наиболее распространены следующие синтаксисы:
Язык | Оператор возврата | Если значение опущено, верните |
---|---|---|
Ада , оболочка Борна , [а] C , C++ , Java , PHP , C# , JavaScript , D | return value;
|
В оболочке Bourne значение выхода последней команды, выполненной в функции.
В С [1] и С++, [2] неопределенное поведение , если функция возвращает значение В PHP, [12] возвращает В Javascript, [13] возвращает значение В Java и C# не разрешено, если функция возвращает значение. |
БАЗОВЫЙ | RETURN
|
|
Лисп | (return value)
|
Значение последнего оператора |
Перл , Руби | return @values;
return $value;
return;
или контекстная возвращаемая последовательность |
Значение последнего оператора |
ПЛ/Я |
return(expression); return; |
Неопределенное поведение , если процедура объявлена как возвращающая значение |
Питон | return value
|
None [4]
|
Смолток | ^ value
|
|
Ткл | return
return $value
return -code error "Error message"
или какая-то более сложная комбинация опций |
Значение последнего оператора |
Визуальный Бейсик .NET | Return value
|
|
Windows PowerShell | return value;
|
Объект |
сборка х86 | ret
|
Содержимое регистра eax (по условию) |
В некоторых языках ассемблера , например, в MOS Technology 6502 , используется мнемоника «RTS» (ReTurn from Subroutine).
Множественные операторы возврата [ править ]
Языки с явным оператором возврата создают возможность использования нескольких операторов возврата в одной и той же функции. Хорошо это или нет, вопрос спорный.
Убежденные приверженцы структурного программирования следят за тем, чтобы каждая функция имела один вход и один выход (SESE). Так утверждалось [14] что следует избегать использования явного оператора return, за исключением текстового конца подпрограммы, учитывая, что, когда он используется для «раннего возврата», он может столкнуться с теми же проблемами, которые возникают с оператором GOTO . И наоборот, можно утверждать, что использование оператора return имеет смысл, когда альтернативой является более запутанный код, например, более глубокая вложенность, ухудшающая читаемость.
В своем учебнике 2004 года Дэвид Уотт пишет, что «часто желательны потоки управления с одним входом и множеством выходов». Теннента Используя понятие секвенсора , Ватт единообразно описывает конструкции потока управления, встречающиеся в современных языках программирования, и пытается объяснить, почему определенные типы секвенсоров предпочтительнее других в контексте потоков управления с несколькими выходами. Ватт пишет, что неограниченные переходы (секвенсоры переходов) плохи, потому что пункт назначения перехода не является самоочевидным для читателя программы до тех пор, пока читатель не найдет и не исследует фактическую метку или адрес, который является целью перехода. Напротив, Уотт утверждает, что концептуальная цель возвращаемого секвенсора ясна из его собственного контекста, без необходимости изучения его назначения. Более того, Ватт пишет, что класс секвенсоров, известный как escape-секвенсоры , определяемый как «секвенсор, который завершает выполнение текстовой команды или процедуры», включает в себя как выходы из циклов (включая многоуровневые разрывы), так и операторы возврата. Уотт также отмечает, что, хотя секвенсоры переходов (гото) были несколько ограничены в таких языках, как C, где целью должен быть внутренний локальный блок или охватывающий внешний блок, одного этого ограничения недостаточно, чтобы сделать назначение переходов в C самостоятельным. -описываю и так их еще можно производить" «Код спагетти ». Уотт также исследует, чем секвенсоры исключений отличаются от секвенсоров escape и jump; подробнее об этом см. статью о структурном программировании. [15]
Согласно эмпирическим исследованиям, на которые ссылается Эрик С. Робертс , студентам-программистам было трудно сформулировать правильные решения для нескольких простых задач на таком языке, как Паскаль, который не допускает множественных точек выхода. Что касается проблемы написания функции для линейного поиска элемента в массиве, исследование Генри Шапиро 1980 года (цитируемое Робертсом) показало, что при использовании только управляющих структур, предоставленных Паскалем, правильное решение дали только 20% испытуемых. , хотя ни один субъект не написал неправильный код для этой проблемы, если разрешено писать возврат из середины цикла. [16]
Другие, в том числе Кент Бек и Мартин Фаулер, утверждают, что одно или несколько защитных предложений — условные операторы возврата «раннего выхода» в начале функции — часто делают функцию более читаемой, чем альтернативу. [17] [18] [19] [20]
Наиболее распространенной проблемой при раннем выходе является то, что операторы очистки или заключительные операторы не выполняются — например, выделенная память не освобождается от распределения или открытые файлы не закрываются, что приводит к утечкам. Это необходимо делать на каждом участке возврата, поскольку он хрупкий и может легко привести к ошибкам. Например, на более поздних стадиях разработки разработчик может пропустить оператор возврата, а действие, которое должно быть выполнено в конце подпрограммы (например, оператор трассировки ), может выполняться не во всех случаях. Языки без оператора возврата, такие как стандартный Паскаль, не имеют этой проблемы. В некоторых языках, таких как C++ и Python, используются концепции, которые позволяют автоматически выполнять действия при возврате (или выдаче исключения), что смягчает некоторые из этих проблем — они часто известны как «попробуй/наконец» или что-то подобное. Функциональность, подобная этим предложениям «finally», может быть реализована путем перехода к единственной точке возврата подпрограммы. Альтернативное решение — использовать обычное развертывание стека (освобождение переменной) при выходе из функции для освобождения ресурсов, например, с помощью деструкторов локальных переменных или аналогичных механизмов, таких как оператор Python «with».
Некоторые ранние реализации языков, такие как исходный Паскаль и C, ограничивали типы, которые могут быть возвращены функцией (например, не поддерживая типы записей или структур ), чтобы упростить их компиляторы .
В Java — и подобных ему языках, таких как JavaScript , — можно выполнять код даже после оператора return, поскольку «finally» блок структуры try-catch всегда выполняется. Таким образом, если оператор return находится где-то внутри блоков try или catch, код внутри него, наконец (если он добавлен), будет выполнен. Можно даже изменить возвращаемое значение не примитивного типа (свойство уже возвращенного объекта), поскольку выход также происходит позже. [21]
Отчеты о доходности [ править ]
Двоюродным братом оператора return являются операторы доходности : если возврат приводит , к завершению подпрограммы то выход приводит сопрограммы приостановке работы к . Сопрограмма позже продолжит работу с того места, где она была приостановлена, если ее вызвать снова. Сопрограммы гораздо сложнее реализовать, чем подпрограммы, поэтому операторы доходности встречаются реже, чем операторы возврата, но они встречаются во многих языках.
Последовательности вызова/возврата [ править ]
В зависимости от набора аппаратных инструкций возможен ряд возможных последовательностей вызова/возврата, включая следующие:
- The
CALL
Инструкция помещает адрес следующей инструкции в стек и переходит к указанному адресу.RETURN
Инструкция извлекает адрес возврата из стека в указатель инструкции, и выполнение возобновляется по этому адресу. (Примеры: x86 , PDP-11 ). В таких архитектурах, как Motorola 96000 , область стека может быть выделена в отдельном адресном пространстве, которое называется «Пространство памяти стека». [22] отличается от адресного пространства основной памяти. [23] NEC μPD7720 также имеет стек с собственным отдельным адресным пространством. [24] - The
CALL
Инструкция помещает адрес следующей инструкции в регистр и переходит к указанному адресу.RETURN
Последовательность команд помещает адрес возврата из регистра в указатель инструкции, и выполнение возобновляется по этому адресу. (Примеры: IBM System/360 и его преемники до z/Architecture , большинство RISC- архитектур) - The
CALL
Инструкция помещает адрес следующей (или текущей ) инструкции в ячейку памяти по адресу вызова и переходит к указанному адресу+1.RETURN
Последовательность команд переходит к адресу возврата путем косвенного перехода к первой инструкции подпрограммы. (Примеры: IBM 1130 , SDS 9XX , PDP-8 )
См. также [ править ]
Примечания [ править ]
Ссылки [ править ]
- ^ Jump up to: Перейти обратно: а б «Оператор возврата (C)» . Документы Майкрософт . 25 января 2023 г.
- ^ Jump up to: Перейти обратно: а б «Оператор возврата (C++)» . Документы Майкрософт . 3 августа 2021 г.
- ^ Гливс, Р. (2012). Модуль-2 для программистов на Паскале . Спрингер. п. 71. ИСБН 9781461385318 .
- ^ Jump up to: Перейти обратно: а б с Мартелли, Алекс (2006). Python в двух словах: краткий справочник по рабочему столу (2-е изд.). О'Рейли Медиа. п. 73. ИСБН 9781449379100 .
- ^ Jump up to: Перейти обратно: а б Скотт, Майкл Л. (2006). Прагматика языков программирования . Морган Кауфманн. п. 432. ИСБН 9780126339512 .
- ^ Фландрия, Харли (2012). Научный Паскаль . Спрингер. п. 35. ISBN 9781461224280 .
- ^ АНСИ х3.9-1966. Стандарт США FORTRAN (PDF) . Американский национальный институт стандартов. п. 14. Архивировано из оригинала (PDF) 15 мая 2011 года . Проверено 5 мая 2010 г.
{{cite book}}
: CS1 maint: числовые имена: список авторов ( ссылка ) - ^ АНСИ х3.9-1978. Американский национальный стандарт – язык программирования FORTRAN . Американский национальный институт стандартов. 15.8 Оператор RETURN. Архивировано из оригинала 29 октября 2013 года . Проверено 11 декабря 2007 г.
{{cite book}}
: CS1 maint: числовые имена: список авторов ( ссылка ) - ^ Саккинен, Маркку (март 1989 г.). «Как лучше всего вернуть значение функции» . Уведомления ACM SIGPLAN . 24 (3). Ассоциация вычислительной техники: 55–56. дои : 10.1145/66083.66087 .
- ^ Вирт, Никлаус (3 мая 2016 г.). «10. Объявления процедур». Язык программирования Оберон (PDF) (Отчет). п. 11.
- ^ «return — возврат из функции или точечного скрипта» . Единая спецификация UNIX .
- ^ «PHP: возврат — вручную» . Руководство по PHP . Группа PHP . Проверено 26 марта 2013 г.
- ^ «Возврат — Javascript» . Справочник по Javascript MDN . Сеть разработчиков Mozilla . Проверено 27 марта 2013 г.
- ^ «Примечания по C++: оператор возврата функции» .
- ^ Ватт, Дэвид Энтони; Финдли, Уильям (2004). Концепции проектирования языков программирования . Джон Уайли и сыновья. стр. 215–221. ISBN 978-0-470-85320-7 .
- ^ Робертс, Э. (март 1995 г.). «Выходы из цикла и структурированное программирование: возобновление дебатов» . Бюллетень ACM SIGCSE . 27 (1): 268–272. дои : 10.1145/199691.199815 .
- ^ Мартин Фаулер; Кент Бек; Джон Брант; Уильям Опдайк; Дон Робертс (2012). Рефакторинг: улучшение дизайна существующего кода (электронная книга Google) . Аддисон-Уэсли. стр. 237, 250. ISBN. 9780133065268 .
... менталитет одной точки выхода ... Я не следую правилу об одной точке выхода из метода.
- ^ Кент Бек (2007). «7: Поведение». Шаблоны реализации . Пирсон Образование. раздел «Охранная статья». ISBN 9780132702553 .
- ^ «Множественные операторы возврата» . Java-практики .
- ^ Фред Шварц. «Операторы возврата и фантазия о единственном выходе» . Архивировано из оригинала 23 февраля 2020 г.
- ^ «Последний блок» . Учебники по Java .
- ^ «РУКОВОДСТВО ПОЛЬЗОВАТЕЛЯ 32-БИТНОГО ПРОЦЕССОРА ЦИФРОВЫХ СИГНАЛОВ DSP96002» (PDF) . п. 27(3 - 4) . Проверено 24 декабря 2023 г.
- ^ «РУКОВОДСТВО ПОЛЬЗОВАТЕЛЯ 32-БИТНОГО ПРОЦЕССОРА ЦИФРОВЫХ СИГНАЛОВ DSP96002» (PDF) . п. 50(4 - 11) . Проверено 24 декабря 2023 г.
- ^ «Процессор цифровых сигналов μPD77C20A, 7720A, 77P20» . п. 4(3а-4) . Проверено 25 декабря 2023 г.