В информатике поток управления (или поток управления ) — это порядок, в котором выполняются или оцениваются отдельные операторы , инструкции или вызовы функций императивной программы . Акцент на явном потоке управления отличает императивный язык программирования от декларативного языка программирования .
В императивном языке программирования оператор потока управления — это оператор, в результате которого делается выбор, какому из двух или более путей следовать. В нестрогих функциональных языках существуют функции и языковые конструкции для достижения одного и того же результата, но их обычно не называют операторами потока управления.
Набор операторов, в свою очередь, обычно структурируется как блок , который помимо группировки также определяет лексическую область действия .
Прерывания и сигналы — это низкоуровневые механизмы, которые могут изменять поток управления аналогично подпрограмме , но обычно возникают как ответ на какой-либо внешний стимул или событие (которое может происходить асинхронно ), а не как выполнение встроенной программы. заявление о потоке управления.
На уровне машинного языка или ассемблера инструкции потока управления обычно работают путем изменения счетчика программ . Для некоторых центральных процессоров (ЦП) единственными доступными инструкциями потока управления являются инструкции условного или безусловного перехода , также называемые переходами.
Виды операторов потока управления, поддерживаемые разными языками, различаются, но их можно классифицировать по их эффекту:
Метка — это явное имя или номер , присвоенный фиксированной позиции в исходном коде , на которое могут ссылаться операторы потока управления, встречающиеся в других местах исходного кода. Метка отмечает позицию в исходном коде и не имеет никакого другого эффекта.
Номера строк являются альтернативой именованной метке, используемой в некоторых языках (например, BASIC ). Это целые числа , помещаемые в начале каждой строки текста исходного кода. Языки, в которых они используются, часто накладывают ограничение, согласно которому номера строк должны увеличиваться в каждой следующей строке, но не могут требовать, чтобы они были последовательными. Например, в БЕЙСИКЕ:
10 БУКУС X = 3 20 ПЕЧАТЬ X
В других языках, таких как C и Ada , метка — это идентификатор , обычно появляющийся в начале строки, за которым сразу следует двоеточие. Например, в С:
Успех : printf ( «Операция прошла успешно. \n » );
Язык АЛГОЛ 60 допускал использование в качестве меток как целых чисел, так и идентификаторов (оба связаны двоеточиями со следующим оператором), но лишь немногие другие варианты АЛГОЛа допускали целые числа. Ранние компиляторы Фортрана допускали использование в качестве меток только целых чисел. Начиная с Фортрана-90, также разрешены буквенно-цифровые метки.
Оператор goto (сочетание английских слов go и to , произносимых соответственно) — это основная форма безусловной передачи управления.
Хотя ключевое слово может быть написано как прописными, так и строчными буквами в зависимости от языка, обычно оно записывается так:
перейти к ярлыку
Эффект оператора goto заключается в том, что следующий оператор будет выполнен как оператор, появляющийся в указанной метке (или сразу после нее).
Многие ученые-компьютерщики, особенно Дейкстра , считали операторы Goto вредными .
Терминология подпрограмм различается; альтернативно они могут быть известны как подпрограммы, процедуры, функции (особенно, если они возвращают результаты) или методы (особенно, если они принадлежат классам или классам типов ).
В 1950 - х годах компьютерная память была очень маленькой по нынешним стандартам, поэтому подпрограммы использовались в основном для уменьшения размера программы. Часть кода была написана один раз, а затем использовалась много раз в разных местах программы.
Сегодня подпрограммы чаще используются, чтобы сделать программу более структурированной, например, изолируя какой-то алгоритм или скрывая какой-то метод доступа к данным. Если над одной программой работают несколько программистов, подпрограммы представляют собой один из видов модульности , которая может помочь разделить работу.
В структурном программировании упорядоченная последовательность последовательных команд считается одной из основных структур управления, которая используется в качестве строительного блока программ наряду с итерацией, рекурсией и выбором.
В мае 1966 года Бём и Якопини опубликовали статью [1] в журнале Communications of the ACM , в которой показано, что любая программа с goto может быть преобразована в форму без goto, включающую только выбор (IF THEN ELSE) и циклы (WHILE условие DO xxx ), возможно, с дублированием кода и/или добавлением логических переменных (флаги true/false). Более поздние авторы показали, что выбор можно заменить циклами (и еще несколькими логическими переменными).
То, что такой минимализм возможен, не означает, что он обязательно желателен; в конце концов, компьютерам теоретически нужна только одна машинная инструкция (вычитание одного числа из другого и разветвление, если результат отрицательный), но практические компьютеры имеют десятки или даже сотни машинных инструкций.
Статья Бема и Якопини показала, что все программы могут быть свободны от операторов goto. Другое исследование показало, что структуры управления с одним входом и одним выходом гораздо легче понять, чем любую другую форму, главным образом потому, что их можно использовать где угодно в качестве оператора, не нарушая поток управления . Другими словами, они были составными . (Более поздние разработки, такие как нестрогие языки программирования , а в последнее время и компонуемые программные транзакции , продолжили эту стратегию, сделав компоненты программ еще более свободно компонуемыми.)
Некоторые ученые придерживались пуристского подхода к результату Бема-Якопини и утверждали, что даже такие инструкции, как break
и return
из середины циклов, являются плохой практикой, поскольку они не нужны в доказательстве Бема-Якопини, и поэтому они выступали за то, чтобы все циклы имели единственную точка выхода. Этот пуристский подход воплощен в языке Паскаль (разработанном в 1968–1969 годах), который до середины 1990-х годов был предпочтительным инструментом для обучения вводному программированию в академических кругах. [2] Прямое применение теоремы Бема–Якопини может привести к введению дополнительных локальных переменных в структурированную карту, а также к некоторому дублированию кода . [3] Паскаль подвержен обеим этим проблемам, и согласно эмпирическим исследованиям, на которые ссылается Эрик С. Робертс , студенты-программисты испытывали трудности с формулировкой правильных решений в Паскале для нескольких простых задач, включая написание функции для поиска элемента в массиве. Исследование Генри Шапиро 1980 года, цитируемое Робертсом, показало, что при использовании только структур управления, предоставляемых Паскалем, правильное решение было дано только 20% испытуемых, в то время как ни один испытуемый не написал неправильный код для этой задачи, если ему было разрешено написать возврат из середина петли. [2]
Большинство языков программирования со структурами управления имеют начальное ключевое слово, которое указывает тип задействованной структуры управления. [ необходимо разъяснение ] Затем языки расходятся во мнениях относительно того, есть ли в структурах управления последнее ключевое слово.
begin
...end
{
...}
DO
...END
do
...end
end
+ пробел + начальное ключевое слово, например, if
... end if
, loop
...end loop
:End
необязательно + начальное ключевое слово, например, :If
... :End
или :If
... :EndIf
, Select
... :End
или :Select
... :EndSelect
, однако при добавлении конечного условия конечное ключевое слово становится:Until
if
... fi
, case
...esac
END
+ начальное ключевое слово, например, IF
... ENDIF
, DO
...ENDDO
END
для всего.If
... End If
; For
... Next
; Do
... Loop
; While
...Wend
Условные выражения и условные конструкции — это функции языка программирования , которые выполняют различные вычисления или действия в зависимости от того, имеет ли заданное программистом логическое условие значение true или false.
IF..GOTO
. Форма, встречающаяся в неструктурированных языках, имитирующая типичную инструкцию машинного кода, будет переходить (GOTO) к метке или номеру строки, когда условие будет выполнено.IF..THEN..(ENDIF)
. Вместо того, чтобы ограничиваться переходом, любой простой оператор или вложенный блок может следовать за ключевым словом THEN. Это структурированная форма.IF..THEN..ELSE..(ENDIF)
. То же, что и выше, но со вторым действием, которое необходимо выполнить, если условие ложно. Это одна из самых распространенных форм, имеющая множество вариаций. Некоторым требуется терминал ENDIF
, другим — нет. C и родственные языки не требуют ключевого слова терминала или «то», но требуют круглых скобок вокруг условия.ELSE
и в , избегая необходимости иметь серию или другие заключительные операторы в конце составного оператора.IF
ELSEIF
ENDIF
Менее распространенные варианты включают:
if
, например в Lisp cond
.if
, например, тернарный оператор C.if
с помощью when
и unless
.ifTrue
и ifFalse
сообщения для реализации условных операторов, а не каких-либо фундаментальных языковых конструкций.Операторы переключения (или операторы Case , или многосторонние ветвления ) сравнивают заданное значение с указанными константами и выполняют действия в соответствии с первой совпадающей константой. Обычно предусмотрено действие по умолчанию («иначе», «иначе»), которое должно быть выполнено, если совпадение не удалось. Операторы переключения могут обеспечивать оптимизацию компилятора, например таблицы поиска . В динамических языках регистры могут не ограничиваться константными выражениями и могут расширяться до сопоставления с образцом , как в примере сценария оболочки справа, где *)
реализует регистр по умолчанию в виде glob , соответствующего любой строке. Логика регистра также может быть реализована в функциональной форме, как в операторе SQLdecode
.
Цикл — это последовательность операторов, которая указывается один раз, но может выполняться несколько раз подряд. Код «внутри» цикла (тело цикла , показанное ниже как xxx ) выполняется заданное количество раз, либо один раз для каждого набора элементов, либо до тех пор, пока не будет выполнено какое-либо условие, либо бесконечно .
В языках функционального программирования , таких как Haskell и Scheme , как рекурсивные , так и итеративные процессы выражаются с помощью процедур хвостовой рекурсии вместо циклических конструкций, которые являются синтаксическими.
В большинстве языков программирования есть конструкции для повторения цикла определенное количество раз. В большинстве случаев счет может идти вниз, а не вверх, и можно использовать размер шага, отличный от 1.
В этих примерах, если N < 1, тело цикла может выполняться один раз (при этом I имеет значение 1) или не выполняться вообще, в зависимости от языка программирования.
Во многих языках программирования в циклах, управляемых счетом, можно надежно использовать только целые числа. Числа с плавающей запятой представляются неточно из-за аппаратных ограничений, поэтому такой цикл, как
для X := 0,1 шаг от 0,1 до 1,0 сделать
может повторяться 9 или 10 раз, в зависимости от ошибок округления и/или аппаратного обеспечения и/или версии компилятора. Более того, если приращение X происходит путем повторного сложения, накопленные ошибки округления могут означать, что значение X на каждой итерации может весьма существенно отличаться от ожидаемой последовательности 0,1, 0,2, 0,3,..., 1,0.
В большинстве языков программирования есть конструкции для повторения цикла до тех пор, пока не изменится какое-либо условие. Некоторые варианты проверяют условие в начале цикла; другие проверяют это в конце. Если тест стоит на старте, то тело можно вообще пропустить; если оно находится в конце, тело всегда выполняется хотя бы один раз.
Разрыв управления — это метод обнаружения изменения значения, используемый в обычных циклах для запуска обработки групп значений. Значения отслеживаются внутри цикла, и изменение переключает поток программы на обработку связанного с ними группового события.
ДЕЛАТЬ ДО (конца файла) ЕСЛИ новый почтовый индекс <> текущий почтовый индекс display_tally (текущий почтовый индекс, почтовый индекс) текущий почтовый индекс = новый почтовый индекс почтовый индекс = 0 КОНДИФ zipcount++ ПЕТЛЯ
Некоторые языки программирования (например, Ada , D , C++11 , Smalltalk , PHP , Perl , Object Pascal , Java , C# , MATLAB , Visual Basic , Ruby , Python , JavaScript , Fortran 95 и более поздние версии) имеют специальные конструкции, которые позволяют неявно циклический просмотр всех элементов массива или всех членов набора или коллекции.
someCollection do : [:eachElement |xxx]. для элемента в коллекции do Begin xxx End ; foreach (элемент; myCollection) { xxx } Еогеасп someArray { xxx } foreach ($someArray as $k => $v) { xxx } Коллекция<String> coll; for (String s: coll) {} foreach ( строка s в myStringCollection) { xxx } некотораяКоллекция | ForEach-Object { $_ } forall (индекс = первый:последний:шаг...)
В Scala есть выражения for , которые обобщают циклы, управляемые коллекциями, а также поддерживают другие применения, например асинхронное программирование . В Haskell есть do-выражения и comprehension, которые вместе выполняют функции, аналогичные for-выражениям в Scala.
Общие конструкции итерации, такие как for
оператор C и форма Common Lisp, могут do
использоваться для выражения любого из вышеупомянутых типов циклов, а также других, таких как параллельный цикл по некоторому количеству коллекций. Там, где можно использовать более конкретную конструкцию цикла, она обычно предпочтительнее общей конструкции итерации, поскольку она часто проясняет цель выражения.
Бесконечные циклы используются для обеспечения зацикливания сегмента программы навсегда или до тех пор, пока не возникнет исключительная ситуация, например ошибка. Например, программа, управляемая событиями (например, сервер ), должна работать вечно, обрабатывая события по мере их возникновения и останавливаясь только тогда, когда процесс завершается оператором.
Бесконечные циклы могут быть реализованы с использованием других конструкций потока управления. Чаще всего в неструктурированном программировании это переход назад (goto), тогда как в структурированном программировании это бесконечный цикл (цикл while), который никогда не заканчивается, либо путем пропуска условия, либо явно устанавливая для него значение true, как while (true) ...
. В некоторых языках есть специальные конструкции для бесконечных циклов, обычно путем исключения условия из неопределенного цикла. Примеры включают Ada ( loop ... end loop
), [4] Fortran ( DO ... END DO
), Go ( for { ... }
) и Ruby ( loop do ... end
).
Часто бесконечный цикл непреднамеренно создается из-за ошибки программирования в цикле, управляемом по условию, где условие цикла использует переменные, которые никогда не изменяются внутри цикла.
Иногда внутри тела цикла возникает желание пропустить оставшуюся часть тела цикла и перейти к следующей итерации цикла. В некоторых языках предусмотрен оператор, например continue
(большинство языков), skip
, [5] cycle
(Фортран) или next
(Perl и Ruby), который делает это. В результате выполнение самого внутреннего тела цикла преждевременно завершается, а затем возобновляется в обычном режиме со следующей итерации. Если итерация является последней в цикле, результатом будет досрочное завершение всего цикла.
В некоторых языках, таких как Perl [6] и Ruby, [7] есть redo
оператор, который перезапускает текущую итерацию с самого начала.
В Ruby есть retry
инструкция, которая перезапускает весь цикл с начальной итерации. [8]
При использовании цикла, управляемого счетчиком, для поиска по таблице может оказаться желательным остановить поиск, как только нужный элемент будет найден. Некоторые языки программирования, например break
(большинство языков), Exit
(Visual Basic) или last
(Perl), предоставляют оператор, результатом которого является немедленное завершение текущего цикла и передача управления оператору сразу после этого цикла. Другой термин для циклов с ранним выходом — «полтора цикла» .
Следующий пример выполнен на языке Ada , который поддерживает как ранний выход из циклов , так и циклы с тестом в середине . Обе функции очень похожи, и сравнение обоих фрагментов кода покажет разницу: ранний выход должен сочетаться с оператором if , а условие в середине является автономной конструкцией.
с Ada.Text IO ; с Ada.Integer Text IO ;процедура Print_Squares равна X : Integer ; начать Read_Data : цикл Ada . Целочисленный текстовый ввод-вывод . Получить ( Х ); выйти из Read_Data, когда X = 0 ; Ада . Текст ИО . Поместите ( Х * Х ); Ада . Текст ИО . Новая линия ; завершить цикл Read_Data ; конец Print_Squares ;
Python поддерживает условное выполнение кода в зависимости от того, был ли цикл завершен раньше (с помощью break
оператора) или нет, с помощью предложения else в цикле. Например,
for n в set_of_numbers : if isprime ( n ): print ( «Набор содержит простое число» ), Break else : print ( «Набор не содержит простых чисел» )
Предложение else
в приведенном выше примере связано с for
утверждением, а не с внутренним if
утверждением. И Python, for
и while
циклы поддерживают такое предложение else, которое выполняется только в том случае, если не произошло досрочного выхода из цикла.
Некоторые языки поддерживают выход из вложенных циклов; в теоретических кругах это называется многоуровневыми разрывами. Одним из распространенных примеров использования является поиск в многомерной таблице. Это можно сделать либо с помощью многоуровневых разрывов (выход из N уровней), как в bash [9] и PHP, [10] , либо с помощью помеченных разрывов (выход и продолжение по заданной метке), как в Java и Perl. [11] Альтернативы многоуровневым разрывам включают одиночные разрывы вместе с переменной состояния, которая проверяется на прорыв другого уровня; исключения, которые перехватываются на уровне, на который осуществляется прорыв; помещение вложенных циклов в функцию и использование возврата для завершения всего вложенного цикла; или используя метку и оператор перехода. C не включает многоуровневый разрыв, и обычной альтернативой является использование перехода для реализации помеченного разрыва. [12] В Python нет многоуровневого разрыва или продолжения — это было предложено в PEP 3136 и отклонено на том основании, что добавленная сложность не стоит редкого законного использования. [13]
Идея многоуровневых разрывов представляет определенный интерес в теоретической информатике , поскольку она порождает то, что сегодня называется иерархией Косараджу . [14] В 1973 году С. Рао Косараджу уточнил теорему о структурированной программе , доказав, что можно избежать добавления дополнительных переменных в структурном программировании, если разрешены многоуровневые выходы из циклов произвольной глубины. [15] Кроме того, Косараджу доказал, что существует строгая иерархия программ: для каждого целого числа n существует программа, содержащая многоуровневый разрыв глубины n , которую нельзя переписать как программу с многоуровневыми разрывами глубины меньше n . без введения дополнительных переменных. [14]
Можно также return
выйти из подпрограммы, выполняющей зацикленные операторы, выйдя как из вложенного цикла, так и из подпрограммы. Существуют и другие предлагаемые структуры управления для множественных перерывов, но они обычно реализуются в виде исключений.
В своем учебнике 2004 года Дэвид Уотт использует понятие секвенсора Теннента , чтобы объяснить сходство между многоуровневыми разрывами и операторами возврата. Ватт отмечает, что класс секвенсоров, известный как escape-секвенсоры , определяемый как «секвенсор, который завершает выполнение текстовой команды или процедуры», включает в себя как выходы из циклов (включая многоуровневые разрывы), так и операторы возврата. Однако в обычном исполнении секвенсоры возврата также могут нести (возвратное) значение, тогда как секвенсор прерывания, реализованный в современных языках, обычно не может. [16]
Варианты цикла и инварианты цикла используются для выражения правильности циклов. [17]
На практике вариант цикла представляет собой целочисленное выражение, имеющее начальное неотрицательное значение. Значение варианта должно уменьшаться во время каждой итерации цикла, но никогда не должно становиться отрицательным во время правильного выполнения цикла. Варианты цикла используются, чтобы гарантировать завершение цикла.
Инвариант цикла — это утверждение, которое должно быть истинным до первой итерации цикла и оставаться истинным после каждой итерации. Это означает, что при корректном завершении цикла выполняются как условие выхода, так и инвариант цикла. Инварианты цикла используются для мониторинга определенных свойств цикла во время последовательных итераций.
Некоторые языки программирования, такие как Eiffel , содержат встроенную поддержку вариантов и инвариантов циклов. В других случаях поддержка является дополнением, например, спецификацией языка моделирования Java для операторов цикла в Java .
Некоторые диалекты Лиспа предоставляют расширенный подъязык для описания циклов. Ранний пример можно найти в Conversional Lisp of Interlisp . Common Lisp [18] предоставляет макрос Loop, реализующий такой подъязык.
while (true)
не считается бесконечным циклом, поскольку он не является специальной языковой структурой.for (init; test; increment)
range()
while
для этого можно использовать функцию.std::for_each
шаблона , которая может перебирать контейнеры STL и вызывать унарную функцию для каждого элемента. [19] Функциональность также может быть реализована в виде макроса в этих контейнерах. [20]retry
, однако оно используется при обработке исключений , а не при управлении циклом.GO TO
и процедур.Многие языки программирования, особенно те, которые предпочитают более динамичные стили программирования, предлагают конструкции для нелокального потока управления . Это приводит к тому, что поток выполнения выскакивает из заданного контекста и возобновляется в какой-то заранее объявленной точке. Условия , исключения и продолжения — это три распространенных типа нелокальных управляющих конструкций; существуют и более экзотические, такие как генераторы , сопрограммы и ключевое слово async .
PL/I имеет около 22 стандартных условий (например, ZERODIVIDE SUBSCRIPTRANGE ENDFILE), которые могут быть вызваны и перехвачены с помощью: действия условия ON; Программисты также могут определять и использовать свои собственные именованные условия.
Как и в случае с неструктурированным if , можно указать только один оператор, поэтому во многих случаях требуется GOTO, чтобы решить, где следует возобновить поток управления.
К сожалению, некоторые реализации имели значительные накладные расходы как по пространству, так и по времени (особенно SUBSCRIPTRANGE), поэтому многие программисты старались избегать использования условий.
Общие примеры синтаксиса:
Состояние ВКЛ Метка GOTO
Современные языки имеют специализированную структурированную конструкцию для обработки исключений, которая не зависит от использования GOTO
(многоуровневых) прерываний или возвратов. Например, в C++ можно написать:
try { xxx1 // Где-то здесь xxx2 // используйте: '''throw''' someValue; xxx3 } catch ( someClass & someId ) { // перехватываем значение someClass actionForSomeClass } catch ( someType & otherId ) { // перехватываем значение someType actionForSomeType } catch (...) { // перехватываем все, что еще не перехвачено actionForAnythingElse }
catch
Выше можно использовать любое количество и разнообразие предложений. catch
Если совпадение с конкретным объектом не найдено throw
, управление передается обратно через вызовы подпрограмм и/или вложенные блоки до тех пор, пока не будет найдено соответствие catch
или пока не будет достигнут конец основной программы, после чего программа принудительно останавливается с подходящим сообщением об ошибке.
Под влиянием C++ catch
это ключевое слово, зарезервированное для объявления обработчика исключений сопоставления с образцом в других популярных сегодня языках, таких как Java или C#. Некоторые другие языки, такие как Ada, используют это ключевое слово exception
для введения обработчика исключений, а затем могут даже использовать другое ключевое слово ( when
в Ada) для сопоставления с образцом. Некоторые языки, такие как AppleScript, включают заполнители в синтаксис обработчика исключений для автоматического извлечения нескольких фрагментов информации при возникновении исключения. Этот подход иллюстрируется ниже конструкцией on error
из AppleScript:
попробуйте установить myNumber в myNumber / 0 при ошибке e номер n от f до t частичный результат pr if ( e = «Невозможно разделить на ноль» ) , затем отобразить диалоговое окно «Вы не должны этого делать» и завершить попытку
В учебнике Дэвида Уотта 2004 года также анализируется обработка исключений в рамках секвенсоров (представленная в этой статье в разделе о ранних выходах из циклов). Уотт отмечает, что ненормальная ситуация, обычно иллюстрируемая арифметическими переполнениями или ошибками ввода/вывода, например, файл не найден, является своего рода ошибкой, которая «обнаруживается в каком-то низкоуровневом программном модуле, но [для которого] более естественным образом расположен обработчик. в программном модуле высокого уровня». Например, программа может содержать несколько вызовов чтения файлов, но действие, которое необходимо выполнить, когда файл не найден, зависит от значения (назначения) рассматриваемого файла для программы, и поэтому процедура обработки этой ненормальной ситуации не может быть создана. находится в низкоуровневом системном коде. Уоттс далее отмечает, что введение тестирования флагов состояния в вызывающем объекте, как это повлечет за собой структурное программирование с одним выходом или даже секвенсоры возврата (с несколькими выходами), приводит к ситуации, когда «код приложения имеет тенденцию загромождаться проверками флагов состояния» и что «программист может по забывчивости или лениво не проверять флаг состояния. Фактически, нештатные ситуации, представленные флагами состояния, по умолчанию игнорируются!» Ватт отмечает, что в отличие от тестирования флагов состояния, исключения имеют противоположное поведение по умолчанию , вызывая завершение работы программы, если только программа не обрабатывает исключение каким-либо явным образом, возможно, путем добавления явного кода для его игнорирования. Основываясь на этих аргументах, Уотт заключает, что секвенсоры перехода или escape-секвенсоры менее подходят в качестве специализированного секвенсора исключений с семантикой, описанной выше. [21]
В Object Pascal, D, Java, C# и Python finally
к конструкции можно добавить предложение try
. Независимо от того, как управление покидает try
код, код внутри finally
предложения гарантированно выполнится. Это полезно при написании кода, который должен освободить дорогостоящий ресурс (например, открытый файл или соединение с базой данных) после завершения обработки:
FileStream stm = ноль ; // Пример C# try { stm = new FileStream ( " logfile.txt" , FileMode.Create ) ; вернуть ProcessStuff ( stm ); // может вызвать исключение } наконец { if ( stm != null ) stm . Закрывать (); }
Поскольку этот шаблон довольно распространен, C# имеет специальный синтаксис:
использование ( вар stm = новый FileStream ( «logfile.txt» , FileMode . Create )) { return ProcessStuff ( stm ); // может вызвать исключение }
После выхода из using
-блока компилятор гарантирует, что stm
объект будет освобожден, эффективно привязывая переменную к файловому потоку и абстрагируясь от побочных эффектов инициализации и освобождения файла. Оператор Python with
и аргумент блока Ruby File.open
используются для аналогичного эффекта.
Все упомянутые выше языки определяют стандартные исключения и обстоятельства, при которых они выдаются. Пользователи могут создавать собственные исключения; C++ позволяет пользователям генерировать и перехватывать практически любые типы, включая базовые типы, такие как int
, тогда как другие языки, такие как Java, менее либеральны.
В C# 5.0 появилось ключевое слово async для поддержки асинхронного ввода-вывода в «прямом стиле».
Генераторы , также известные как полукорутины, позволяют временно передавать управление потребительскому методу, обычно с использованием yield
ключевого слова (описание вывода). Как и ключевое слово async, оно поддерживает программирование в «прямом стиле».
Сопрограммы — это функции, которые могут передавать управление друг другу — форма совместной многозадачности без потоков.
Сопрограммы могут быть реализованы в виде библиотеки, если язык программирования предоставляет либо продолжения, либо генераторы, поэтому на практике различие между сопрограммами и генераторами является технической деталью.
В ложной статье Datamation [28] в 1973 году Р. Лоуренс Кларк предположил, что оператор GOTO можно заменить оператором COMEFROM , и привел несколько занимательных примеров. COMEFROM был реализован на одном эзотерическом языке программирования под названием INTERCAL .
В статье Дональда Кнута 1974 года «Структурное программирование с переходом к операторам» [29] определены две ситуации, которые не были охвачены структурами управления, перечисленными выше, и приведены примеры структур управления, которые могли бы справиться с этими ситуациями. Несмотря на свою полезность, эти конструкции еще не нашли своего применения в основных языках программирования.
Следующее было предложено Далем в 1972 году: [30]
петля петля xxx1 чтение (символ); во время теста; пока не в EndOfFile; xxx2 запись (символ); повторить ; повторить ;
Если xxx1 опущен, мы получим цикл с проверкой вверху (традиционный цикл while ). Если xxx2 опущен, мы получим цикл с проверкой внизу, что эквивалентно циклу do while во многих языках. Если while опущено, мы получим бесконечный цикл. Конструкцию здесь можно рассматривать как цикл do с проверкой while посередине. Следовательно, эта единственная конструкция может заменить несколько конструкций в большинстве языков программирования.
Языки, в которых отсутствует эта конструкция, обычно эмулируют ее, используя эквивалентную идиому бесконечного цикла с разрывом:
в то время как (истина) { ххх1 если ( не тест) сломается ххх2}
Возможный вариант — разрешить более одного теста во время теста; внутри цикла, но использование exitwhen (см. следующий раздел), по-видимому, лучше описывает этот случай.
В Ada приведенная выше конструкция цикла ( loop- while - repeat ) может быть представлена с помощью стандартного бесконечного цикла ( loop - end Loop ), который имеет предложение выхода When в середине (не путать с оператором выхода When в следующем разделе). ).
с Ada.Text_IO ; с Ada.Integer_Text_IO ;процедура Print_Squares равна X : Integer ; начать Read_Data : цикл Ada . Целое_Текст_IO . Получить ( Х ); выйти из Read_Data, когда X = 0 ; Ада . Текст ИО . Поместите ( Х * Х ); Ада . Текст ИО . Новая линия ; завершить цикл Read_Data ; конец Print_Squares ;
Именование цикла (например, Read_Data в этом примере) не является обязательным, но позволяет оставить внешний цикл из нескольких вложенных циклов.
Эта конструкция была предложена Заном в 1974 году. [31] Здесь представлена модифицированная версия.
выйти, когда EventA или EventB или EventC; ххх выходит Событие А: действие А Событие Б: действие Б СобытиеC: действиеC конечный выход ;
exitwhen используется для указания событий, которые могут произойти в пределах xxx , их появление указывается с использованием имени события в качестве оператора. Когда какое-то событие все-таки происходит, выполняется соответствующее действие, а затем управление передается сразу после endexit . Эта конструкция обеспечивает очень четкое разделение между определением того, что какая-то ситуация применима, и действием, которое необходимо предпринять в этой ситуации.
exitwhen концептуально похож на обработку исключений , и во многих языках для этой цели используются исключения или подобные конструкции.
Следующий простой пример включает поиск определенного элемента в двумерной таблице.
выход при обнаружении или пропаже; для I := от 1 до N сделать для J := от 1 до M сделать если таблица[I,J] = цель, затем найдена; отсутствующий; выходит найдено: печать ("элемент находится в таблице"); отсутствует: печать («элемента нет в таблице»); конечный выход ;
Одним из способов атаки на часть программного обеспечения является перенаправление потока выполнения программы. Для защиты от этих атак используются различные методы обеспечения целостности потока управления , в том числе канарейки стека , защита от переполнения буфера , теневые стеки и проверка указателя виртуальной таблицы . [32] [33] [34]