Книга: Linux программирование в примерах
12.5.1. Использование стандартных функций: setjmp() и longjmp()
12.5.1. Использование стандартных функций: setjmp()
и longjmp()
Нелокальные переходы осуществляются с помощью функций setjmp()
и longjmp()
. Эти функции используются в двух разновидностях. Традиционные процедуры определены стандартом ISO С:
#include <setjmp.h> /* ISO С */
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
Тип jmp_buf
определен через typedef
в <setjmp.h>
. setjmp()
сохраняет текущее «окружение» в env
. env
обычно является глобальной или статической на уровне файла переменной, так что она может использоваться из вызванной функции. Это окружение включает любую информацию, необходимую для перехода на местоположение, из которого была вызвана setjmp()
. Содержание jmp_buf
по своей природе машинно-зависимо; таким образом, jmp_buf
является непрозрачным типом: тем, что вы используете, не зная, что находится внутри него.
setjmp()
возвращает 0, когда она вызывается для сохранения в jmp_buf
текущего окружения. Ненулевое значение возвращается, когда с использованием окружения осуществляется нелокальный переход:
jmp_buf command_loop; /* На глобальном уровне */
осуществляет переход. Первым параметром является
/* ... затем в main() ... */
if (setjmp(command_loop) == 0) /* Состояние сохранено, продолжить */
;
else /* Мы попадаем сюда через нелокальный переход */
printf("?n"); /* ed's famous message */
/* ... теперь начать цикл команд ... */
longjmp()jmp_buf
, который должен быть инициализирован с помощью setjmp()
. Второй является целым ненулевым значением, которое setjmp()
возвращает в первоначальное окружение. Это сделано так, что код, подобный только что показанному, может различить установку окружения и прибытие путем нелокального перехода.
Стандарт С утверждает, что даже если longjmp()
вызывается со вторым аргументом, равным 0, setjmp()
по-прежнему возвращает ненулевое значение. В таком случае она возвращает 1.
Возможность передать целое значение и вернуться обратно из setjmp()
полезна; это позволяет коду уровня пользователя различать причину перехода. Например, gawk
использует эту возможность для обработки операторов break
и continue
внутри циклов. (Язык awk осознанно сделан похожим на С в своем синтаксисе для циклов, с использованием while
, do-while
, for
, break
и continue
.) Использование setjmp()
выглядит следующим образом (из eval.c
в дистрибутиве gawk
3.1.3):
507 case Node_K_while:
508 PUSH_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
509
510 stable_tree = tree;
511 while (eval_condition(stable_tree->lnode)) {
512 INCREMENT(stable_tree->exec_count);
513 switch (setjmp(loop_tag)) {
514 case 0: /* обычный не переход */
515 (void)interpret(stable_tree->rnode);
516 break;
517 case TAG_CONTINUE: /* оператор continue */
518 break;
519 case TAG_BREAK: /* оператор break */
520 RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
521 return 1;
522 default:
523 cant_happen();
524 }
525 }
526 RESTORE_BINDING(loop_tag_stack, loop_tag, loop_tag_valid);
527 break;
Этот фрагмент кода представляет цикл while
. Строка 508 управляет вложенными циклами посредством стека сохраненных переменных jmp_buf
. Строки 511–524 выполняют цикл while
(используя цикл С while
!). Строка 511 проверяет условие цикла. Если оно истинно, строка 513 выполняет switch
на возвращаемое значение setjmp()
. Если оно равно 0 (строки 514–516), строка 515 выполняет тело оператора. Однако, когда setjmp()
возвращает TAG_BREAK
или TAG_CONTINUE
, оператор switch
обрабатывает их соответствующим образом (строки 517–518 и 519–521 соответственно).
Оператор break
на уровне awk
передает TAG_BREAK
функции longjmp()
, a continue
уровня awk
передает TAG_CONTINUE
. Снова из eval.c
с некоторыми пропущенными не относящимися к делу подробностями:
657 case Node_K_break:
658 INCREMENT(tree->exec_count);
/* ... */
675 longjmp(loop_tag, TAG_BREAK);
676 break;
677
678 case Node_K_continue:
679 INCREMENT(tree->exec_count);
/* ... */
696 longjmp(loop_tag, TAG_CONTINUE);
670 break;
Вы можете думать о setjmp()
как об установке метки, а о longjmp()
как выполнении goto
с дополнительным преимуществом возможности сказать, откуда «пришел» код (по возвращаемому значению).
- Восстановление с использованием инструмента gbak
- Типы страниц и их использование
- Использование констант
- Использование переменной окружения ISC_PATH
- Использование сервера Yaffil внутри процесса
- Использование CAST() с типами дата
- Использование типов содержимого и столбцов
- Вызов хранимых процедур InterBase с использованием стандартного синтаксиса ODBC
- Использование кнопки Автосумма
- 24.7. Использование программы-твикера
- Использование отдельных процессоров XSLT
- 4. Использование подзапросов