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

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 безопасны в только следующих контекстах:

  • Как тестовое выражение в операторе выбора или цикла (типа "if" или "while").
  • Как один операнд равенства или сравнения, которое появляется как тестовое выражение оператора выбора или цикла. Другой операнд должен быть целочисленным постоянным выражением.
  • Как операнд унарного оператора "!", который появляется как как тестовое выражение оператора выбора или цикла.
  • Как выражение утверждения.
Пункты возврата, допустимы только в течение динамической протяженности функции которая вызвала 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 также восстанавливает набор блокированных сигналов.


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

Наш баннер
Вы можете установить наш баннер на своем сайте или блоге, скопировав этот код:
RSS новости