现在的位置: 首页 > 综合 > 正文

setjmp longjmp

2014年02月15日 ⁄ 综合 ⁄ 共 2647字 ⁄ 字号 评论关闭

int setjmp(jmp_buf jmpb) 设置缓冲区来保存堆栈的内容,将保存的上下文存入进程的自身的数据空间(u区),并继续在当前的上下文中执行,一旦碰到了longjmp,进城就从该进程 的u区,取出先前保存的上下文,并恢复该进程的上下文为先前保存的上下文。这时核心将使得进程从setjmp处执行(摘自:unix平台下c语言高级编程
指南)

setjmp和longjmp的函数原型在setjmp.h中。
函数原型:
int setjmp(jmp_buf envbuf);
setjmp函数用缓冲区envbuf保存系统堆栈的内容,以便后续的longjmp函数使用。setjmp函数初次启用时返回0值。

void longjmp(jmp_buf envbuf, int val);
longjmp函数中的参数envbuf是由setjmp函数所保存的堆栈环境,参数val设置setjmp函数的返回值。longjmp函数本身是没有返回值的,它执行后跳转到保存envbuf参数的setjmp函数调用,并由setjmp函数调用返回,此时setjmp函数的返回值就是val。
调用longjmp函数时不能使setjmp函数返回0,如果val为0,则setjmp函数返回1。longjmp函数从来不返回,因为它调用后就跳转到setjmp函数保存的堆栈处,恢复堆栈开始执行,所以longjmp函数不会返回。


void longjmp(jmp_buf jmpb, int val) 使进程返回到 setjmp处执行,retval 表示此时setjmp的返回值。

longjmp必须在setjmp调用之后,而且longjmp必须在setjmp的作用域之内。具体来说,在一个函数中使用setjmp来初始化 一个全局标号,然后只要该函数未曾返回,那么在其它任何地方都可以通过longjmp调用来跳转到 setjmp的下一条语句执行。实际上setjmp函数将发生调用处的局部环境保存在了一个jmp_buf的结构当中,只要主调函数中对应的内存未曾释放 (函数返回时局部内存就失效了),那么在调用longjmp的时候就可以根据已保存的jmp_buf参数恢复到setjmp的地方执行。
setjmp函数的返回值(直接返回时为0,longjmp跳转返回时为longjmp的状态参数retval,根据setjmp的返回值就可以判断程序是 正常执行还是进行异常处理。
一个setjmp可以有多个longjmp。 setjmp用于初始化跳转变量(相当于打了一个标记,声明"用本跳转变量的longjmp就跳到我这里来啦"),longjmp用于跳转。 
setjmp可以多次返回,返回0表示初始化跳转变量完成(也称直接返回);返回其他值表示有函数调用了longjmp, 这时的返回值是longjmp设置的。 所以setjmp语句后有一个switch-case块,根据setjmp返回的不同值进行不同的处理。
setjmp()和longjum()是通过操纵过程活动记录实现的。许多程序员新手(包括我-o-)并不知道这个强大的机制,因为它是C语言所独有的。它们部分你不了C语言有限的转移能力。这个两个函数协同工作,如下所示:
*setjmp(jmp_buf j)必须首先被调用。它表示“使用变量j记录现在的位置。函数返回零。”
*longjmp(jmp_buf j,int i)可以接着被调用。它表示“回到j所记录的位置,让它看上去像是从原来的setjmp()函数返回一样。但是函数返回i,使代码知道它实际上是通过longjmp()返回的。“坳口不?
*当使用longjmp()时,j的内容被销毁。
setjmp保存了一份程序的计数器和当前的栈顶指针。如果喜欢也可以保存一些初始值。longjmp恢复这些值,有效的转移控制并把状态重置回保存状态的时候。这被称做“展开堆栈(unwinding stack)",因为你从堆栈中展开过程活动记录,直到取得保存在其中的值。尽管longjmp会导致转移,但它和goto又有不同,区别如下:
*goto语句不能跳出C语言当前的函数(这也是“longjmp”取名的由来,它可以跳的很远,甚至可以跳到其他文件的函数中)。
*用longjmp只能跳回到曾经到过的地方。在setjmp的地方仍留有一个过程活动记录。从这个角度讲,longjmp更像是“从何处阿里(come from)“而不是”往哪里去(go to)”。longjmp接受一个额外的整型参数并返回它的值,这可以知道是由longjmp转移到这里的还是从上条语句执行后自然而然来的这里的。
下面的代码显示了setjmp()和longjmp()一例。
#include 
#include 
jmp_buf buf;

banana() {
printf("%s","in banana() \n");
longjmp(buf,1);
printf("%s","you will never see this \n");
}

int main() {
if(setjmp(buf)) {
printf("%s","back in main\n");
}
else {
printf("%s","first time throught\n");
banana();
}
}
输出结果如下:
first time throught
in banana()
back in main
需要注意的地方是:保证局部变量在longjmp过程中一直保持它的值的唯一可靠方法是把它声明为volatile(这使用于那些值在setjmp执行和longjmp返回之间会改变的变量)
另外一个例子:
setjmp和longjmp函数使用示例:
#include 
#include 

static jmp_buf buf;

int main()
{
int b;
b = 3;

if (setjmp(buf) != 0) 
{
printf("b=%d\n", b); 
return 0;
}
b = 5;
longjmp(buf, 1);

return 0;
}
setjmp执行时返回0,执行b等于5,调用longjmp,跳转到setjmp调用,setjmp返回1,打印b=5。 
setjmp/longjmp最大的用途是错误恢复。只要还没有从函数中返回,一旦发现一个不可恢复的错误,可以把控制转移到主输入循环,并从那里重新开始。有些人使用setjmp/longjmp从一串无数的函数调用中立即返回。还有些人用它们防范潜在的危险代码。
setjmp/longjmp在C++中演变为更普通的异常处理机制"catch"和"throw"

抱歉!评论已关闭.