Вперед Назад Содержание

20. Нелокальные Выходы

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

20.1 Введение в нелокальные Выходы

Вот пример ситуации, где нелокальный выход может быть полезен: предположим, Вы имеете интерактивную программу, которая имеет "основной цикл" который запрашивает и выполняет команды. Предположите, что команда "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 /* Код, который будет выполнен после обычной установки возвращающей отметки. */ . . .

20.2 Подробности нелокальных Выходов

Имеются некоторые подробности относительно функций и структур данных, используемых для выполнения нелокальных выходов.

Эти средства объявлены в " setjmp.h ".

jmp_buf (тип данных)
Объекты типа jmp_buf содержат информацию о состоянии, которое будет восстановлено при нелокальном выходе.

Содержимое jmp_buf идентифицирует конкретное место возвращения.

int setjmp (jmp_buf state) (макрос)
setjmp сохраняет информацию относительно состояния выполнения программы в state и возвращает нуль. Если longjmp позже используется, чтобы выполнить нелокальный выход к этому состоянию, setjmp возвращает значение отличное от нуля.
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 являются неопределенными, если Вы не объявили их отдельно.

20.3 Нелокальные Выходы и Сигналы

В системах 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 также восстанавливает набор блокированных сигналов.


Вперед Назад Содержание