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>


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章