Next Previous Contents

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


Next Previous Contents