WikiDer > Setjmp.h
Эта статья нужны дополнительные цитаты для проверка. (Декабрь 2016 г.) (Узнайте, как и когда удалить этот шаблон сообщения) |
Стандартная библиотека C |
---|
Общие темы |
Разные заголовки |
setjmp.h это заголовок определено в Стандартная библиотека C для обеспечения «нелокальных прыжков»: поток управления что отличается от обычного подпрограмма последовательность вызова и возврата. Дополнительные функции setjmp
и longjmp
обеспечить эту функциональность.
Типичное использование setjmp
/longjmp
реализация механизм исключения который использует способность longjmp
для восстановления состояния программы или потока даже на нескольких уровнях вызовов функций. Менее распространенное использование setjmp
заключается в создании синтаксиса, подобного сопрограммы.
Функции-члены
интервал setjmp (jmp_buf env) | Устанавливает местный jmp_buf буфер и инициализирует его для перехода. Эта рутина[1] сохраняет среду вызова программы в буфере среды, заданном параметром env аргумент для последующего использования longjmp . Если возврат от прямого вызова, setjmp возвращает 0. Если возврат от вызова longjmp , setjmp возвращает ненулевое значение. |
void longjmp (jmp_buf env, значение int) | Восстанавливает контекст буфера среды env который был спасен призывом setjmp рутина[1] в том же вызове программы. Вызов longjmp из вложенного обработчика сигналов неопределенный. Значение, указанное ценить передается из longjmp к setjmp . После longjmp завершена, выполнение программы продолжается, как если бы соответствующий вызов setjmp только что вернулся. Если ценить перешел к longjmp равно 0, setjmp будет вести себя так, как если бы он вернул 1; в противном случае он будет вести себя так, как если бы он вернулся ценить . |
setjmp
сохраняет текущую среду (состояние программы) в какой-то момент выполнения программы в структуру данных платформы (jmp_buf
), который может быть использован на более позднем этапе выполнения программы longjmp
восстановить состояние программы до сохраненного setjmp
в jmp_buf
. Этот процесс можно представить как «прыжок» назад к точке выполнения программы, где setjmp
спасла окружающую среду. (Очевидный) возвращаемое значение из setjmp
указывает, достигло ли управление этой точки нормально (ноль) или от вызова до longjmp
(ненулевое). Это приводит к общему идиома: если( setjmp(Икс) ){/ * обрабатываем longjmp (x) * /}
.
POSIX.1 не указывает, setjmp
и longjmp
сохранить и восстановить текущий набор заблокированных сигналы; если программа использует обработку сигналов, она должна использовать POSIX sigsetjmp
/siglongjmp
.
Типы участников
jmp_buf | Тип массива, например struct __jmp_buf_tag [1] ,[2] подходит для хранения информации, необходимой для восстановления среды вызова. |
Обоснование C99 описывает jmp_buf
как тип массива для Обратная совместимость; существующий код относится к jmp_buf
места хранения по имени (без &
адрес оператора), что возможно только для типов массивов.[3]
Предостережения и ограничения
Когда "нелокальный переход" выполняется через setjmp
/longjmp
в C ++, нормальный "разматывание стека"не происходит. Следовательно, никакие требуемые действия по очистке также не выполняются. Это может включать закрытие файловые дескрипторы, промывка буферы, или освобождение память, выделенная кучей.
Если функция, в которой setjmp
был вызван возвратом, уже нельзя безопасно использовать longjmp
с соответствующими jmp_buf
объект. Это потому, что кадр стека становится недействительным, когда функция возвращается. Вызов longjmp
восстанавливает указатель стека, который - поскольку функция вернула - будет указывать на несуществующий и потенциально перезаписанный или поврежденный фрейм стека.[4][5]
По аналогии, C99 не требует этого longjmp
сохранить текущий кадр стека. Это означает, что переход к функции, которая была завершена вызовом longjmp
не определено.[6] Однако большинство реализаций longjmp
оставить фрейм стека нетронутым, позволяя setjmp
и longjmp
использоваться для переключения между двумя или более функциями - функция, используемая для многозадачность.
По сравнению с механизмами в языках программирования более высокого уровня, таких как Python, Ява, C ++, C #, и даже языки до C, такие как Алгол 60, техника использования setjmp
/longjmp
реализовать механизм исключения громоздко. Эти языки предоставляют более мощные Обработка исключений методы, в то время как языки, такие как Схема, Болтовня, и Haskell предоставить даже более общий продолжение-управляемые конструкции.
Пример использования
Простой пример
В приведенном ниже примере показана основная идея setjmp. Там, главный()
звонки первый()
, который, в свою очередь, вызывает второй()
. Потом, второй()
прыгает обратно в главный()
, пропуская первый()
зов printf ()
.
#включают <stdio.h>#включают <setjmp.h>статический jmp_buf бух;пустота второй() { printf("второй п"); // печатает longjmp(бух,1); // возвращается туда, где был вызван setjmp - теперь setjmp возвращает 1}пустота первый() { второй(); printf("первый п"); // не печатает}int главный() { если (!setjmp(бух)) первый(); // при выполнении setjmp вернул 0 еще // когда longjmp перескакивает назад, setjmp возвращает 1 printf("главный п"); // печатает возвращаться 0;}
При выполнении вышеуказанная программа выведет:
второй главный
Обратите внимание, что хотя первый()
вызывается подпрограмма "первый
"никогда не печатается".главный
"печатается как условное выражение если (! setjmp (buf))
выполняется второй раз.
Обработка исключений
В этом примере setjmp
используется для скобок обработки исключений, например пытаться
на некоторых других языках. Призыв к longjmp
аналогичен бросать
оператор, позволяющий исключению возвращать статус ошибки непосредственно в setjmp
. Следующий код соответствует Стандарт ISO 1999 г. и Единая спецификация UNIX ссылаясь на setjmp
в ограниченном диапазоне контекстов:[7]
- Как условие
если
,выключатель
или оператор итерации - Как указано выше в сочетании с одиночным
!
или сравнение с целочисленной константой - Как утверждение (с неиспользованным возвращаемым значением)
Следование этим правилам может упростить реализацию для создания буфера среды, что может быть деликатной операцией.[3] Более общее использование setjmp
может вызвать неопределенное поведение, например повреждение локальных переменных; соответствующие компиляторы и среды не обязаны защищать или даже предупреждать о таком использовании. Однако несколько более сложные идиомы, такие как переключатель ((тип_исключения = setjmp (env))) {}
распространены в литературе и на практике и остаются относительно портативными. Ниже представлена простая соответствующая методология, в которой вместе с буфером состояний поддерживается дополнительная переменная. Эта переменная может быть преобразована в структуру, включающую сам буфер.
В более современном примере обычный блок «try» будет реализован как setjmp (с некоторым кодом подготовки для многоуровневых прыжков, как показано на первый
), «throw» as longjmp с необязательным параметром в качестве исключения и «catch» в качестве блока «else» в разделе «try».
#включают <setjmp.h>#включают <stdio.h>#включают <stdlib.h>#включают <string.h>статический пустота первый();статический пустота второй();/ * Используйте статическую переменную с файловой областью для стека исключений, чтобы мы могли получить доступ * это где угодно в пределах этой единицы перевода. * /статический jmp_buf exception_env;статический int исключение_типа;int главный(пустота) { char* летучий mem_buffer = НОЛЬ; если (setjmp(exception_env)) { // если мы попали сюда, произошло исключение printf("первая ошибка, тип исключения:% d п", исключение_типа); } еще { // Запускаем код, который может сигнализировать об ошибке через longjmp. ставит("сначала звоню"); первый(); mem_buffer = маллок(300); // выделяем ресурс printf("% s п", strcpy(mem_buffer, "сначала удалось")); // не достиг } свободный(mem_buffer); // NULL можно передать в free, никаких операций не выполняется возвращаться 0;}статический пустота первый() { jmp_buf my_env; ставит("входящий первым"); // достиг memcpy(my_env, exception_env, размер my_env); выключатель (setjmp(exception_env)) { дело 3: // если мы попали сюда, произошло исключение. ставит("второй сбой, тип исключения: 3; переназначение на тип 1"); исключение_типа = 1; дефолт: // провалиться memcpy(exception_env, my_env, размер exception_env); // восстанавливаем стек исключений longjmp(exception_env, исключение_типа); // продолжаем обработку исключения дело 0: // нормальная, желаемая операция ставит("вызывающий второй"); // достиг второй(); ставит("второй преуспел"); // не достиг } memcpy(exception_env, my_env, размер exception_env); // восстанавливаем стек исключений ставит("уходящий первым"); // никогда не достигал}статический пустота второй() { ставит("второй вход" ); // достиг исключение_типа = 3; longjmp(exception_env, исключение_типа); // объявляем, что программа потерпела неудачу ставит("уходящий второй"); // не достиг}
Результат этой программы:
вызов первого вызова второй вызов второй секунды завершился неудачно, тип исключения: 3; переназначение на тип 1 сначала не удалось, тип исключения: 1
Хотя исключение_типа
Переменная технически здесь не нужна, поскольку setjmp возвращает то ненулевое значение, с которым был вызван longjmp (как во втором и первом), на практике будет использоваться более сложный глобальный объект для размещения более богатых исключений.
В реальном мире setjmp-longjmp (sjlj) использовался как стандартный способ обработки исключений в сторонних компиляторах Windows C ++ (а именно MinGW), так как родной Структурированная обработка исключений в целом плохо документирован, а также был запатентован для 32-битной Windows до 2014 года.
Кооперативная многозадачность
C99 предусматривает, что longjmp
гарантированно работает только тогда, когда адресатом является вызывающая функция, т. е. что область назначения гарантированно не повреждена. Переход к функции, которая уже завершилась возвращаться
или же longjmp
не определено.[6] Однако большинство реализаций longjmp
не уничтожайте специально локальные переменные при выполнении прыжка. Поскольку контекст сохраняется до тех пор, пока его локальные переменные не будут стерты, его можно восстановить с помощью setjmp
. Во многих средах (например, Действительно простые темы и TinyTimbers), идиомы, такие как если (! setjmp (child_env)) longjmp (caller_env);
может позволить вызываемой функции эффективно приостановить и возобновить работу в setjmp
.
Это используется библиотеками потоков для предоставления совместная многозадачность объекты без использования setcontext
или другой волокно удобства. В то время как setcontext
- это библиотечная служба, которая может создавать контекст выполнения в памяти, выделенной кучей, и может поддерживать другие службы, такие как защита от переполнения буфера,[нужна цитата] злоупотребление setjmp
реализуется программистом, который может зарезервировать память в стеке и не уведомить библиотеку или операционную систему о новом рабочем контексте. С другой стороны, реализация библиотеки setcontext
может использовать внутри setjmp
аналогично этому примеру для сохранения и восстановления контекста после того, как он был каким-то образом инициализирован.
Учитывая, что setjmp
для дочерней функции обычно будет работать, если не саботируется, и setcontext
, как часть POSIX, не требуется, чтобы его предоставляли реализации C, этот механизм может быть переносимым, если setcontext
альтернатива не удалась.
Поскольку при переполнении одного из нескольких стеков в таком механизме исключение не создается, важно переоценить пространство, необходимое для каждого контекста, включая тот, который содержит главный()
и включая место для любых обработчиков сигналов, которые могут прервать обычное выполнение. Превышение выделенного пространства повредит другие контексты, обычно в первую очередь внешние функции. К сожалению, системы, требующие такой стратегии программирования, также часто бывают небольшими с ограниченными ресурсами.
#включают <setjmp.h>#включают <stdio.h>jmp_buf основная задача, childTask;пустота call_with_cushion();пустота ребенок();int главный() { если (!setjmp(основная задача)) { call_with_cushion(); // потомок никогда не возвращается, yield } // выполнение возобновляется после этого "}" после первого раза, когда этот дочерний элемент дает пока (1) { printf("Родитель п"); если (!setjmp(основная задача)) longjmp(childTask, 1); // yield - обратите внимание, что это не определено в C99 }}пустота call_with_cushion() { char Космос[1000]; // Зарезервировать достаточно места для запуска main Космос[999] = 1; // Не убирать оптимизируемый массив ребенок();}пустота ребенок() { пока (1) { printf("Начало дочернего цикла п"); если (!setjmp(childTask)) longjmp(основная задача, 1); // yield - аннулирует childTask в C99 printf("Дочерний цикл конец п"); если (!setjmp(childTask)) longjmp(основная задача, 1); // yield - аннулирует childTask в C99 } / * Не возвращать. Вместо этого мы должны установить флаг, чтобы указать, что main () должен перестать уступать нам и тогда longjmp (mainTask, 1) * /}
Смотрите также
Рекомендации
- ^ а б ISO C утверждает, что
setjmp
должен быть реализован как макрос, но в POSIX явно указано, что не определено, будет лиsetjmp
это макрос или функция. - ^ Это тип, используемый Библиотека GNU C, версия 2.7
- ^ а б Обоснование C99, версия 5.10, апрель 2003 г., раздел 7.13
- ^ Лекции CS360 - Setjmp и Longjmp
- ^ setjmp (3) В архиве 2009-07-26 на Wayback Machine
- ^ а б ISO / IEC 9899: 1999, 2005, 7.13.2.1:2 и сноска 211
- ^ Единая спецификация UNIX, Выпуск 7 из Открытая группа : установить точку перехода для нелокального перехода - Справочник по системным интерфейсам,
внешняя ссылка
- Единая спецификация UNIX, Выпуск 7 из Открытая группа : установить точку перехода для нелокального перехода - Справочник по системным интерфейсам,
- Исключения в C с Longjmp и Setjmp
- есть ли sigsetjmp / siglongjmp (снова) (об этой функции в Mingw/MSYS)