setjmp和longjmp用法


本文轉自:http://blog.csdn.net/wuhong40/article/details/6155838,感謝原文作者。

前不久在閱讀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

繼續讀吧,在代碼中慢慢體會吧...


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM