我正在调查 setjmp / longjmp ,发现 setjmp 会保存指令,如指令指针,堆栈指针等...
然而,我没有得到的是,在调用 setjmp 和 longjmp 之间,不能修改线程堆栈中的数据本身。在这种情况下, longjmp 不会按预期工作。
为了说清楚,例如,当 longjmp 恢复堆栈指针时,说现在堆栈指针所指向的内存中的数据与 setjmp 被叫了。这会发生吗?如果发生这种情况,我们不是有麻烦吗?
语句的意思是什么,“在调用setjmp()例程的例程返回后,可能无法调用longjmp()例程。”
答案 0 :(得分:6)
setjmp()/longjmp()
并不意味着保存堆栈,这就是setcontext()/getcontext()
的用途。
该标准指定在setjmp()
和setjmp()
调用之间调用longjmp()
的函数中定义的非易失性自动变量的值未指定{{ 1}}。由于同样的原因,您对longjmp()
的调用方式也有一些限制。
答案 1 :(得分:5)
堆栈指针标记堆栈的“已使用”和“未使用”部分之间的划分。当您拨打setjmp
时,所有当前的呼叫帧都在“已使用”一侧,并且在setjmp
之后但在呼叫setjmp
的功能返回之前发生的任何呼叫都会进行呼叫保存的堆栈指针的“未使用”侧的帧。请注意,在调用longjmp
的函数返回后调用setjmp
会调用未定义的行为,因此不需要考虑这种情况。
现在,某些现有调用帧中的局部变量可能会在setjmp
之后被调用函数或指针修改,这就是为什么必须使用volatile
的原因之一在许多情况下......
答案 2 :(得分:3)
C中的setjmp / longjmp(以下简称slj)功能很难看,其行为可能因实现而异。尽管如此,由于没有例外,在C中有时需要slj(请注意,C ++提供的异常几乎在所有方面都优于slj,并且slj与许多C ++特性交互得很厉害。)
在使用slj时,应该记住以下内容,假设例程Parent()调用例程Setter(),它调用setjmp()然后调用Jumper,后者又调用longjmp()。
尽管setjmp / longjmp()有时可能有用,但它们也可能非常危险。在大多数情况下,没有保护错误的代码导致未定义的行为,并且在许多现实世界的情况下,不正确的使用可能会导致不良事件发生(不像某些类型的未定义行为,其中实际结果可能经常与程序员打算)。
答案 3 :(得分:0)
在下面的例子中,setjmp / longjump通过指针改变生活在main中的i的值。我永远不会在for循环中递增。有关额外的乐趣,请参阅1992年IOCCC的albert.c,http://www.ioccc.org/years-spoiler.html条目。 (我曾几次ROTFLED阅读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);
}