Шрифт:
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
с дополнительным преимуществом возможности сказать, откуда «пришел» код (по возвращаемому значению). 12.5.2. Обработка масок сигналов:
sigsetjmp
и siglongjmp
По историческим причинам, которые, скорее всего, утомили бы вас до слез, стандарт С 1999 г. ничего не говорит о влиянии
setjmp
и longjmp
на состояние сигналов процесса, а POSIX явно констатирует, что их влияние на маску сигналов процесса (см. раздел 10.6 «Сигналы POSIX») не определено. Другими словами, если программа изменяет свою маску сигналов процесса между первым вызовом
setjmp
и вызовом longjmp
, каково состояние маски сигналов процесса после longjmp
? Та ли эта маска, когда была впервые вызвана setjmp
? Или это текущая маска? POSIX явно утверждает, что «нет способа это узнать».