=========================================== 轉載一 =====================================================
轉自: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>