Иногда, когда ваша программа обнаруживает необычную ситуацию
внутри глубоко вложенного набора обращений к функциям, Вы можете захотеть
немедленно возвратиться к внешнему уровню управления. Этот раздел
описывает, как делать такие нелокальные выходы,
используя setjmp и longjmp функции.
Вот пример ситуации, где нелокальный выход может быть полезен:
предположим, Вы имеете интерактивную программу, которая имеет "основной цикл"
который запрашивает и выполняет команды.
Предположите, что команда "read" читает ввод из файла, делая
некоторый лексический анализ и анализируя ввод. Если входная ошибка
низкого уровня обнаружена, то было бы полезно возвратиться
немедленно в " основной цикл " вместо того, чтобы иметь
необходимость делать лексический анализ, синтаксический анализ, и
все фазы обработки, которые должны явно иметь дело с ситуациями
ошибки, первоначально обнаруженными вложенными обращениями.
Некоторым образом нелокальный выход подобен использованию
"return", чтобы возвратиться из функции. Но в то время как "return"
отказывается только от обращения, пересылая управление обратно к
функции, из которой оно вызывалось, нелокальный выход может
потенциально отказываться от многих уровней вложенных обращений к
функциям.
Вы определяете куда возвращать управление при нелокальных выходах,
вызывая функцию setjmp. Эта функция сохраняет информацию
относительно среды выполнения, в которой появляется обращение к
setjmp в объекте типа jmp_buf. После обращения к setjmp выполнение
программы продолжается как обычно, но если позже вызывается
longjmp с соответствующим объектом jmp_buf, управление передается
обратно в то место, где вызывалась setjmp. Возвращаемое значение из
setjmp используется, чтобы отличить обычный возврат и возврат,
сделанный обращением к longjmp, так что обращения к setjmp обычно
появляются в ` if '.
Вот пример программы, описанный выше:
#include <setjmp.h> #include <stdlib.h> #include <stdio.h> jmp_buf main_loop; void abort_to_main_loop (int status) { longjmp (main_loop, status); } int main (void) { while (1) if (setjmp (main_loop)) puts ("Back at main loop...."); else do_command (); } void do_command (void) { char buffer[128]; if (fgets (buffer, 128, stdin) == NULL) abort_to_main_loop (-1); else exit (EXIT_SUCCESS); }
Функция abort_to_main_loop вызывает непосредственную передачу
управления в main программы, независимо от того, где она
вызывается.
Способ управления внутри функции main может показаться сначала немного
таинственным, но это - фактически общая идиома для setjmp.
Нормальное обращение к setjmp возвращает нуль, так что "else"-часть
условного выражения выполнена. Если abort_to_main_loop вызывается
где-нибудь внутри выполнения команды do, то это фактически
действует как будто обращение к setjmp в main возвращалось со
значением -1.
Так, общий шаблон для использования setjmp выглядит вроде:
if (setjmp (buffer)) /* Код, для выполнения после преждевременного возврата. */ . . . else /* Код, который будет выполнен после обычной установки возвращающей отметки. */ . . .
Имеются некоторые подробности относительно функций и структур
данных, используемых для выполнения нелокальных выходов.
Эти средства объявлены в " setjmp.h ".
jmp_buf (тип данных)
Объекты типа jmp_buf содержат информацию о состоянии, которое
будет восстановлено при нелокальном выходе.
Содержимое jmp_buf идентифицирует конкретное место
возвращения.
int setjmp (jmp_buf state) (макрос)
void longjmp (jmp_buf state, int value)
Эта функция восстанавливает текущее выполнение в состояние,
сохраненное в state, и продолжает выполнение от обращения к setjmp.
Возвращение из setjmp посредством longjmp возвращает значение
аргумента, который был передан к longjmp, а не 0. (Но если значение
задано как 0, setjmp возвращает 1).
Имеется множество неизвестных, но важных ограничений на
использование setjmp и longjmp. Большинство этих ограничений
присутствует, потому что нелокальные выходы требуют некоторых
волшебных свойств от части компилятора Cи и могут взаимодействовать
с другими частями языка странными способами.
setjmp - фактически макрокоманда без определения функции, так что
Вы не должны пробовать к " #undef " ее или брать адрес. Кроме того,
обращения к setjmp безопасны в только следующих контекстах:
Пункты возврата, допустимы только в течение динамической
протяженности функции которая вызвала setjmp, установить их. Если
Вы используете longjmp чтобы возвратиться отметке, которая была
установлена в функции, которая уже возвратилась, могут случиться
непредсказуемые и бедственные вещи.
Вы должны использовать в longjmp аргумент отличный от нуля. То
что longjmp отказывается передавать обратно аргумент нуля как
возвращаемое значение из setjmp, предназначено для
безопасности при случайном неправильном употреблении и не
является хорошим стилем программирования.
Когда Вы выполняете нелокальный выход, все доступные объекты
сохраняют любые значения, которые они имели во время вызова
longjmp. Исключение - значения динамических локальных переменных,
локальных для функции, содержащей обращение к setjmp, будут
изменены и начиная с обращения на setjmp являются неопределенными,
если Вы не объявили их отдельно.
В системах UNIX BSD, setjmp и longjmp также сохраняют и
восстанавливают набор блокированных сигналов; см. Раздел 21.7
[Блокирование Сигналов]. Однако, POSIX.1 стандарт требует чтобы
setjmp и longjmp не изменяли набор блокированных сигналов, и
обеспечивает дополнительную пару функций (sigsetjmp и sigsetjmp)
чтобы получить поведение BSD функций.
Поведение setjmp и longjmp в библиотеке GNU управляется
макрокомандами теста возможностей; см. Раздел 1.3.4 [Макрокоманды
Проверки Возможностей]. Значение по умолчанию в системе GNU
POSIX.1 поведение, а не поведение BSD.
Средства в этом разделе объявлены в заглавном файле " setjmp.h ".
sigjmp_buf (тип данных)
Подобен jmp_buf, за исключением того, что он может также
сохранять информацию о состоянии набора блокированных сигналов.
int sigsetjmp (sigjmp_buf state, int savesigs) (функция)
Подобна setjmp. Если savesigs отличен от нуля, набор
блокированных сигналов сохранен в state и будет восстановлен, если
siglongjmp позже будет выполнена с этим state.
void siglongjmp (sigjmp_buf state, int value) (функция)
Подобна longjmp кроме типа аргумента state. Если обращение к
sigsetjmp, которое устанавило это состояние, использовало savesigs
флаг отличный от нуля, siglongjmp также восстанавливает набор
блокированных сигналов.