setjmp与longjmp结合使用时,它们必须有严格的先后执行顺序,也即先调用setjmp函数,之后再调用longjmp函数,以恢复到先前被保存的“程序执行点”。(从哪儿来,回哪儿去)否则,如果在setjmp调用之前,执行longjmp函数,将导致程序的执行流变的不可预测,很容易导致程序崩溃而退出。
1.人们对于goto语句的忌讳,很多的专业书籍以及专业人士号召限制goto语句的使用,此时,setjmp与longjmp对goto语句有了很好的替代作用.
2.goto语句有一个局限性,它只能在函数内部跳转.而setjmp与longjmp可以在整个程序全局中跳转,实现"长跳转",弥补了goto功能的局限.
3.使用setjmp和longjmp可以捕捉程序中的异常,并采取异常处理机制.
一个例子 使用setjmp,longjmp处理异常.
C语言具有的类似try and catch机制,unix下机制也相同。
VC例子:
#include <stdio.h>
#include <setjmp.h>
jmp_buf mark; // 保存stack环境的变量
void main(void)
{
int jmpret;
int result;
jmpret = setjmp( mark ); @1@ // 保存当前stack环境到mark变量
if( jmpret == 0 )
{
scanf("%d",&result);
if(result<0) {
longjmp( mark, -1 ); // 发生错误,恢复保存的stack环境,执行跳转到@1@处,jmpret的值为-1
return;
// 此语句永远不被执行
} else {
printf("Normal!");
return;
}
} else {
// 错误处理
printf("An exception occured!");
}
}
longjmp在Standard C中的说明:
1.用longjmp跳转时保证静态变量有正确的值
2.auto变量只保证拥有volatile类型和在setjmp和longjmp之间没有改变的值。
解释:
1.静态变量保持为在longjmp调用时的值
2.在setjmp和longjmp之间没有改变的值仍然是原值,(很简单,因为从来就没有改变过)
对于一些auto-nonvolatile的值,可能在循环中,编译器为了优化性能,用寄存器来缓存,longjmp跳转时没有赋值,用volatile修饰能阻止编译器优化。
这两个函数都要包含头文件setjmp.h。而且它们在处理出现在深层函数嵌套的错误情况时很有用处。 longjmp注意:
1.不要假象寄存器类型的变量将总会保持不变。在调用longjmp之后,通过setjmp所返回的控制流中,例程中寄存器类型的变量将不会被恢复。
2.不要使用longjmp函数来实现把控制流,从一个中断处理例程中传出,除非被捕获的异常是一个浮点数异常。在后一种情况下,如果程序通过调用 _fpreset函数,来首先初始化浮点数包后,它是可以通过longjmp来实现从中断处理例程中返回。
3. 在C++程序中,小心对setjmp和longjmp的使用,应为setjmp和longjmp并不能很好地支持C++中面向对象的语义。因此在C++程序中,使用C++提供的异常处理机制将会更加安全。把setjmp和longjmp组合起来,原来它这么厉害
使用setjmp时必须使用头文件setjmp.h。
#include "setjmp.h"
jmp_buf jmpbuffer;
int setjmp(jmp_buf jmpbuffer);
void longjmp(jmp_buf jumpbuffer, int retval);
其中 jmpbuffer 是相关的程序栈的环境上下文。
初始化jmpbuffer之后, setjmp第一次调用的时候会返回 0。
longjmp跳转到setjmp处,其中第二个参数retval就是传递给setjmp, 作为setjmp的返回值。但是需要主要的是,如果retval设置为0, 即这样调用的时候 longjmp(jumpbuffer, 0), setjmp会返回1。