[<<Previous Entry]
[^^Up^^]
[Next Entry>>]
[Menu]
[About The Guide]
Нелокальный goto
Библиотечная функция longjmp(3C) представляет собой аналог оператора
goto для перехода между функциями. Управление может быть передано
только в точку, которая была помечена вызовом setjmp(3).
setjmp(3), который метит точку для передачи управления, должен
получить в качестве параметра буфер для сохранения текущего контекста
функции в данной точке. longjmp(3) вызывается с тем же самым буфером в
качестве параметра. Исполнение возобновится с положения последнего
setjmp(3) для этого буфера. Значение val будет возвращено setjmp(3)
после выполнения longjmp(3), и должно быть ненулевым.
Тип jmp_buf представляет собой массив целых чисел, описанный с помощью
конструкции typedef. Число элементов в этом массиве - машинно-
зависимо.
Функция longjmp(3) возвращает управление к последнему setjmp(3),
который заполнил буфер env, используемый как аргумент longjmp.
Различие между прямым вызовом setjmp(3) и возвратом к нему через
longjmp(3) состоит в том, что возвращаемое значение будет,
соответственно, нулевым и ненулевым. Если longjmp(3) получит ноль в
качестве val, значение, выданное setjmp(3), будет 1.
Справки: /usr/include/setjmp.h
НЕЛОКАЛЬНЫЙ goto
setjmp(3C)
ИМЯ
setjmp, longjmp - нелокальный goto
ИСПОЛЬЗОВАНИЕ
#include <setjmp.h>
int setjmp (jmp_buf env);
void longjmp (jmp_buf env, int val);
ВОЗВРАЩАЕМОЕ ЗНАЧЕНИЕ
setjmp(3) возвращает 0, когда встречается в выполняемой
последовательности кодов
setjmp(3) возвращает ненулевое значение, если вызов longjmp(3)
привел к переходу к месту вызова setjmp(3)
setjmp(3C) и longjmp(3C) - Пример
Эта программа демонстрирует, каким образом часто используются
setjmp(3C) и longjmp(3C). Эта программа представляет собой циклическое
меню внутри функции main. Вызов setjmp(3C) используется для того,
чтобы пометить точку в программе непосредственно перед циклом.
Замечание: К примеру, ed(1) и ex(1) делают в точности то же самое, так
что если во время исполнения 1,$p будет нажата клавиша <DELETE>,
редактор не возобновляет печать после обработки SIGINT, а выполняет
longjmp к месту приема очередной команды.
Меню в примере содержит следующие команды:
s печатать квадраты целых чисел
t печатать дату и время
q выйти из программы
Для каждой буквы-команды вызывается определенная функция, выполняющая
требуемую операцию. Если пользователь нажимает <DELETE>, программа
продолжается с точки, помеченной setjmp, так как функция обработки
сигнала вызывает longjmp(3), вместо возврата к месту, где находилось
управление до перехвата сигнала.
2-3 Включить <setjmp.h> и объявить переменную env типа jmp_buf
(typedef для массива целых чисел).
8 Сигналы прерывания будут перехватываться и обрабатываться
заданной пользователем функцией ljmain.
9 Текущая точка программы помечена setjmp(3). Возвращаемое
значение будет нулевым. Оно станет ненулевым при последующих
переходах на эту точку при вызове longjmp.
12 Вход в цикл меню.
15-26 Меню вызывает функции, выполняющие требуемые команды.
28-33 Функция обработки сигнала SIGINT переустанавливает сигнал на
ту же функцию (т.е. на себя), печатает "INTERRUPTED" и
выполняет longjmp обратно в функцию main.
... Не показаны sfn(), которая печатает последовательность
квадратов, и tfn(), печатающая дату и время.
Возможно несколько вызовов setjmp(3), позволяющие делать longjmp(3) в
различные точки. Однако, каждый вызов требует своей собственной
переменной jmp_buf.
Этот пример демонстрируется следующим образом:
$ setjmp
cmd: s
0 14916253649
64 81100121144169225
<DELETE>
INTERRUPTED
Before loop
cmd: q
$
Файл: setjmp.c
setjmp(3C) И longjmp(3C) - ПРИМЕР
1 #include <signal.h>
2 #include <setjmp.h>
3 jmp_buf env1;
4 void ljmain(int);
5
6 main() {
7 int retcode;
8 signal(SIGINT, ljmain);
9 retcode = setjmp(env1);
10 if (retcode != 0) printf("\nBefore loop\n");
11
12 while( 1 ) menu();
13 }
14
15 menu() {
16 char resp[2];
17 prompt: printf("cmd: ");
18 scanf("%s",resp);
19 switch(resp[0]){
20 case 's': sfn(); break;
21 case 't': tfn(); break;
22 case 'q': exit(0);
23 default: printf("?\n");
24 goto prompt;
25 }
26 }
27
28 void ljmain(int sig)
29 {
30 signal(SIGINT, ljmain);
31 printf("\nINTERRUPTED\n");
32 longjmp(env1, 1);
33 }
34
...