C 中的 setjmp 和 longjmp 用法

=========================================== 转载一 =====================================================

转自: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传递给setjmplongjmp完成后,程序从对应的setjmp调用处继续执行,如同setjmp调用刚刚完成。如果value传递给longjmp零值,setjmp的返回值为1;否则,setjmp的返回值为value
当使用longjmp的时候,j的内容被销毁。

这种方法看起来与goto相似,但是是有区别的,区别如下:

(1)goto语句不能跳出C语言当前的函数,而longjmp可以跨越多个函数也就是多个栈帧。

(2)用longjmp只能跳回曾经到过的地方。在执行setjmp的地方仍留有一个过程活动记录。从这个角度上讲,longjmp更象是“从何处来”,而不是“要往哪去”。另外,longjmp接受一个额外的整形参数并返回它的值,这可以知道是由longjmp转移到这里的还是从上一条语句执行后自然执行到这里的。

(3)longjmp和setjmp可以有多个组存在,只要han

示例代码:

  1. #include <stdio.h>  
  2. #include <setjmp.h>  
  3.   
  4. jmp_buf buf;  
  5.   
  6. void test_longjmp(void)  
  7. {  
  8.   printf("test_longjmp() \n");  
  9.   longjmp(buf, 1);  
  10.   printf("end test_longjmp()\n");  
  11. }  
  12.   
  13. void main(void)  
  14. {  
  15.    if(setjmp(buf))  
  16.      printf("back test_longjmp");  
  17.    else  
  18.      {  
  19.        printf("first time to setjmp \n");  
  20.        test_longjmp();  
  21.      }  
  22. }  

因为,在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>


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值