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