setjmp和longjmp是C语言独有的,只有将它们结合起来使用,才能达到程序控制流有效转移的目的,按照程序员的预先设计的意图,去实现对程序中可能出现的异常进行集中处理。处理方式是:在调用setjmp()时把当前的堆栈环境保存到jmp_buf(一个结构体类型,下面简单介绍)类型的env中,以后调用longjmp()函数还原所保存的环境并将控制权返回给setjmp之后的调用点。关于这点,《C++编程思想》是这样解析的:使用setjmp()可以在程序中保存一个已知的无错误的状态,一旦发生错误,就可以通过调用longjmp()返回到该状态。同样,这使得错误发生的位置与保存状态的位置之间产生高度的耦合。
jmp_buf结构体介绍:
在setjmp.h里的定义是:typedef struct _jmp_buf { int _jb[_JBLEN + 1]; } jmp_buf[1];
setjmp()函数介绍:
setjmp(j)设置"jump" 点,用正确的程序上下文填充jmp_buf对象j。这个上下文包括程序存放位置、栈和框架指针,其它重要的寄存器和内存数据。当初初始化jump的上下文,setjmp()返回值0。
longjmp()函数介绍:
longjmp函数是还原堆栈环境和执行区域设置的。其原型是:void longjmp(jmp_buf env, int value );参数:env 环境中储存的变量 value 是作为跳到setjmp的调用点时,setjmp()函数的一个返回值。例如下面的例子中,由于longjmp(kansas, 47); 所以,跳到setjmp()时,setjmp必须返回47这个值。
对于学习C++的朋友,有一点需要强调的是:由于setjmp()与longjmp()是属于C语言的函数,所以在C++的错误处理方式中,这两个函数并不会去调用析构函数,所以,当跳过了对象的析构操作时,先前产生的对象就会被正确清理掉(因为longjmp()函数可跳转到析构函数的作用范围以外的地方,这使得程序的行为存在不可预料的情况,对程序而言这绝对是有害的)。
代码如下:
#include <iostream>
#include <csetjmp>
using namespace std;
class NewChange
{
public:
NewChange(){cout<<"NewChange()"<<endl;}
~NewChange(){cout<<"~NewChange()"<<endl;}
};
jmp_buf value;
void oz()
{
NewChange rb;
for(int i = 0; i<3; i++)
cout<<"You are strange!"<<endl;
longjmp(value, 4);
}
int main()
{
/*setjmp()第一次初始化时,把当前处理器的状态信 **
**息保存到jmp_buf中去并返回0,当由于longjmp的调用**
**使执行点跳回到这里时,setjmp就返回longjmp函数的**
**第二个参数4*/
if(setjmp(value)==0)
{
cout<<"I am just ..."<<endl;
oz();
}
else
{
cout<<"I had the strangest dream..."<<endl;
}
return 0;
}
运行结果: