我正在调查setjmp / longjmp ,发现setjmp保存了指令指针,堆栈指针等寄存器。
然而,我没有得到的是,线程本身堆栈中的数据不能在调用setjmp和longjmp之间修改。 在这种情况下,不会长时间不按预期工作。
为了说清楚,例如,当longjmp恢复堆栈指针时,说出堆栈指针现在指向的内存中的数据与setjmp被调用时不同。 这可以发生吗? 如果那样的话,我们不是有麻烦吗?
此外,声明“ longjmp()例程可能不会在调用setjmp()例程的例程返回后调用”。
堆栈指针标记堆栈的“已用”和“未用”部分之间的区分。 当你调用setjmp
,所有当前的调用框架都在“used”一侧,而任何在setjmp
之后但在调用setjmp
的函数之前发生的调用,都会在保存的堆栈指针的“未使用” 。 请注意,在调用setjmp
返回的函数之后调用longjmp
调用未定义的行为,因此不需要考虑这种情况。
现在,一些现有的调用框架中的局部变量可能会在setjmp
之后被调用函数或通过指针来修改,这就是为什么在很多情况下需要使用volatile
原因之一。
setjmp()/longjmp()
并不意味着保存堆栈,这就是setcontext()/getcontext()
的意思。
该标准指定调用setjmp()
函数中定义的非易失性自动变量在setjmp()
和longjmp()
调用之间更改的值在longjmp()
之后未指定。 由于同样的原因,你也有一些限制你调用setjmp()
。
C中的setjmp / longjmp(以下称为slj)特性很丑,其行为在实现中可能会有所不同。 尽管如此,由于没有例外,在C语言中有时需要使用slj(请注意,C ++提供的例外几乎在所有方面都优于slj,并且slj与许多C ++特性的交互不良)。
在使用slj时,应该考虑以下事项,假定例行的Parent()调用例程Setter(),它调用setjmp(),然后调用Jumper,然后调用longjmp()。
尽管setjmp / longjmp()有时可能会有用,但它们也可能非常危险。 在大多数情况下,没有导致未定义行为的保护错误代码,并且在许多现实世界的情况下,不正确的使用可能导致不好的事情发生(不像某些未定义的行为,其中实际结果可能经常与程序员打算)。
这很好地解释了事情。
在下面的例子中,setjmp / longjump通过一个指针改变了i的值,它位于main中。 我永远不会在for循环中增加。 如需额外的乐趣,请参阅1992年IOCCC的获奖者albert.c, http://www.ioccc.org/years-spoiler.html 。 (我曾几次阅读C源代码之一)
#include <stdio.h> #include <setjmp.h> jmp_buf the_state; void helper(int *p); int main (void) { int i; for (i =0; i < 10; ) { switch (setjmp (the_state) ) { case 0: helper (&i) ; break; case 1: printf( "Even=\t"); break; case 2: printf( "Odd=\t"); break; default: printf( "Oops=\t"); break; } printf( "I=%d\n", i); } return 0; } void helper(int *p) { *p += 1; longjmp(the_state, 1+ *p%2); }