Роббинс Арнольд
Шрифт:
Стандарт С утверждает, что даже если
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
с дополнительным преимуществом возможности сказать, откуда «пришел» код (по возвращаемому значению). 12.5.2. Обработка масок сигналов:
sigsetjmp
и siglongjmp
По историческим причинам, которые, скорее всего, утомили бы вас до слез, стандарт С 1999 г. ничего не говорит о влиянии
setjmp
и longjmp
на состояние сигналов процесса, а POSIX явно констатирует, что их влияние на маску сигналов процесса (см. раздел 10.6 «Сигналы POSIX») не определено. Другими словами, если программа изменяет свою маску сигналов процесса между первым вызовом
setjmp
и вызовом longjmp
, каково состояние маски сигналов процесса после longjmp
? Та ли эта маска, когда была впервые вызвана setjmp
? Или это текущая маска? POSIX явно утверждает, что «нет способа это узнать». Чтобы сделать обработку маски сигналов процесса явной, POSIX ввел две дополнительные функции и один
typedef
: #include <setjmp.h> /* POSIX */
int sigsetjmp(sigjmp_buf env, int savesigs); /* Обратите внимание:
sigjmp_buf, не jmp_buf! */
void siglongjmp(sigjmp_buf env, int val);
Главным отличием является аргумент
savesigs
функции sigsetjmp
. Если он не равен нулю, текущий набор заблокированных сигналов сохраняется в env
вместе с остальным окружением, которое сохраняется функцией setjmp
. siglongjmp
с env
, в которой savesigs
содержала true, восстанавливает сохраненную маску сигналов процесса