=========================================== 转载一 =====================================================
转自:http://blog.csdn.net/cscmaker/article/details/7584433
setjmp和longjmp是C语言所独有的,它们部分弥补了C语言有限的转移能力。
函数说明(来自wiki百科):
int setjmp(jmp_buf env) | 建立本地的jmp_buf 缓冲区并且初始化,用于将来跳转回此处。这个子程序保存程序的调用环境于env 参数所指的缓冲区,env 将被longjmp 使用。如果是从setjmp 直接调用返回,setjmp 返回值为0。如果是从longjmp 恢复的程序调用环境返回,setjmp 返回非零值。 |
void longjmp(jmp_buf env, int value) | 恢复env 所指的缓冲区中的程序调用环境上下文,env 所指缓冲区的内容是由setjmp 子程序调用所保存。value 的值从longjmp 传递给setjmp 。longjmp 完成后,程序从对应的setjmp 调用处继续执行,如同setjmp 调用刚刚完成。如果value 传递给longjmp 零值,setjmp 的返回值为1;否则,setjmp 的返回值为value 。 |
这种方法看起来与goto相似,但是是有区别的,区别如下:
(1)goto语句不能跳出C语言当前的函数,而longjmp可以跨越多个函数也就是多个栈帧。
(2)用longjmp只能跳回曾经到过的地方。在执行setjmp的地方仍留有一个过程活动记录。从这个角度上讲,longjmp更象是“从何处来”,而不是“要往哪去”。另外,longjmp接受一个额外的整形参数并返回它的值,这可以知道是由longjmp转移到这里的还是从上一条语句执行后自然执行到这里的。
(3)longjmp和setjmp可以有多个组存在,只要han
示例代码:
- #include <stdio.h>
- #include <setjmp.h>
- jmp_buf buf;
- void test_longjmp(void)
- {
- printf("test_longjmp() \n");
- longjmp(buf, 1);
- printf("end test_longjmp()\n");
- }
- void main(void)
- {
- if(setjmp(buf))
- printf("back test_longjmp");
- else
- {
- printf("first time to setjmp \n");
- test_longjmp();
- }
- }
因为,在test_longjmp中执行了longjmp方法,所以该示例中不会执行到“end test_longjmp()”处。
setjmp/longjmp的最大的用途是错误处理,只要还没有从函数中返回,一旦发现一个不可恢复的错误,可以把控制转移到主输入循环,并从那里重新开始执行。
C++中的异常处理机制"catch"和"throw"与其类似。
=========================================== 转载二 =====================================================
转自:http://blog.163.com/xieke_li/blog/static/363642762009022270798/
前不久在阅读Quake3源代码的时候,看到一个陌生的函数:setjmp,一番google和查询后,觉得有必要针对setjmp和longjmp这对函数写一篇blog,总结一下。
setjmp和longjmp是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。
上面的说明有点拗口,通俗的解释是:先调用setjmp,用变量envbuf记录当前的位置,然后调用longjmp,返回envbuf所记录的位置,并使setjmp的返回值为val。当时用longjmp时,envbuf的内容被销毁了。其实这里的“位置”一词真正的含义是栈定指针。
接着让我们看一个小例子吧:
#include <stdio.h>
#include <setjmp.h>
jmp_buf buf;
banana(){
printf("in banana() \n");
longjmp(buf,1);
printf("you'll never see this,because i longjmp'd");
}
main()
{
if(setjmp(buf))
printf("back in main\n");
else{
printf("first time through\n");
banana();
}
}
(代码段引自《C专家编程》:p)
这段代码的打印结果是:
first time through
in banana()
back in main
仔细看一下应该更能体会这对函数的作用了吧。
setjmp/longjmp的最大用处是错误恢复,类似try ...catch...
他们的功能比goto强多了,goto只能在函数体内跳来跳去,而setjmp/longjmp可以在到过的所有位置间。
从java、.net世界来的兄弟们也许会很不屑于这对函数,也许会觉得这样的功能会使代码的可读性变差。不过请别忘了,这里是C的世界,每个世界有每个世界的哲学,OO只是方法学的一种,而不是全部。quake3是用C写的,据看过其代码的前辈说,其模块化非常好,所以这也是我看quake3代码的初衷。(哦,算了吧,写游戏不是随便说说的...)
注:
我第一次看到setjmp是在quake3代码的Com_Init中,
/*
=================
Com_Init
=================
*/
void Com_Init( char *commandLine ) {
char *s;
Com_Printf( "%s %s %s\n", Q3_VERSION, CPUSTRING, __DATE__ );
if ( setjmp (abortframe) ) {
Sys_Error ("Error during initialization");
}
....
卡马克在这里也是当catch用的,其中的一句注释是这么写的:
jmp_buf abortframe; // an ERR_DROP occured, exit the entire frame
================================================= 下面是自己写的 ====================================================
<span style="font-family:Courier New;">#include<stdio.h>
#include<setjmp.h>
void fun1(void);
void fun2(void);
jmp_buf jmpbuffer1,jmpbuffer2;
int i=0;
int main(void)
{
int flag;
if((flag = setjmp(jmpbuffer1)) !=0)
printf("error %d\n",flag);
printf("...................\n");
if((flag = setjmp(jmpbuffer2)) !=0)
printf("error %d\n",flag);
printf("!!!!!!!!!!!\n");
printf("go fun1.\n");
fun1();
}
void fun1(void)
{
printf("in the fun1 !\n");
if(!i)
{
i = 1; //设置1 视为了避免死循环
longjmp(jmpbuffer2,1);//是为了证明longjmp 是依据jmp_buf 来进行配套的,说明可以多个返回点,
//若是jmpbuffer1 则返回jmpbuffer1 处,否则返回jmpbuffer2 处
printf("out the fun1\n");
fun2();
}
}
void fun2(void)
{
printf("in the fun2 !\n");
longjmp(jmpbuffer2,1);
printf("out the fun2\n");
}</span><span style="color:#ff0000;font-family: 'Hiragino Sans GB W3', 'Hiragino Sans GB', Arial, Helvetica, simsun, u5b8bu4f53; ">
</span>