程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> 關於C語言 >> setjmp()和longjmp()的用法和實現異常處理

setjmp()和longjmp()的用法和實現異常處理

編輯:關於C語言
 

int setjmp(jmp_buf jmpb) 設置緩沖區來保存堆棧的內容,將保存的上下文存入進程的自身的數據空間(u區),並繼續在當前的上下文中執行,一旦碰到了longjmp,進城就從該進程的u區,取出先前保存的上下文,並恢復該進程的上下文為先前保存的上下文。這時核心將使得進程從setjmp處執行.

void longjmp(jmp_buf jmpb, int retval) 使進程返回到 setjmp處執行,retval 表示此時setjmp的返回值。

longjmp必須在setjmp調用之後,而且longjmp必須在setjmp的作用域之內。具體來說,在一個函數中使用setjmp來初始化一個全局標號,然後只要該函數未曾返回,那麼在其它任何地方都可以通過longjmp調用來跳轉到 setjmp的下一條語句執行。實際上setjmp函數將發生調用處的局部環境保存在了一個jmp_buf的結構當中,只要主調函數中對應的內存未曾釋放 (函數返回時局部內存就失效了),那麼在調用longjmp的時候就可以根據已保存的jmp_buf參數恢復到setjmp的地方執行。
setjmp函數的返回值(直接返回時為0,longjmp跳轉返回時為longjmp的狀態參數retval,根據setjmp的返回值就可以判斷程序是正常執行還是進行異常處理。


void main( void )
{
int jmpret;

jmp_buf mark;

jmpret = setjmp( mark );
if( jmpret == 0 )
{
// 其它代碼的執行
// 判斷程序遠行中,是否出現錯誤,如果有錯誤,則跳轉!
if(1) longjmp(mark, 1);

// 其它代碼的執行
// 判斷程序遠行中,是否出現錯誤,如果有錯誤,則跳轉!
if(2) longjmp(mark, 2);

// 其它代碼的執行
// 判斷程序遠行中,是否出現錯誤,如果有錯誤,則跳轉!
if(-1) longjmp(mark, -1);

// 其它代碼的執行
}
else
{
// 錯誤處理模塊
switch (jmpret)
{
case 1:
printf( "Error 1\n");
break;
case 2:
printf( "Error 2\n");
break;
case 3:
printf( "Error 3\n");
break;
default :
printf( "Unknown Error");
break;
}
exit(0);
}

return;
}

  上面的例程非常地簡單,其中程序中使用到了異常處理的機制,這使得程序的代碼非常緊湊、清晰,易於理解。在程序運行過程中,當異常情況出現後,控制流是進行了一個本地跳轉(進入到異常處理的代碼模塊,是在同一個函數的內部),這種情況其實也可以用goto語句來予以很好的實現,但是,顯然setjmp與longjmp的方式,更為嚴謹一些,也更為友善。程序的執行流如圖所示。

setjmp()和longjmp()的用法和實現異常處理 - 下雪了 - 誰的博客


setjmp與longjmp相結合,實現程序的非本地的跳轉

  呵呵!這就是goto語句所不能實現的。也正因為如此,所以才說在C語言中,setjmp與longjmp相結合的方式,它提供了真正意義上的異常處理機制。其實上一篇文章中的那個例程,已經演示了longjmp函數的非本地跳轉的場景。這裡為了更清晰演示本地跳轉與非本地跳轉,這兩者之間的區別,我們在上面剛才的那個例程基礎上,進行很小的一點改動,代碼如下:

void Func1()
{
// 其它代碼的執行
// 判斷程序遠行中,是否出現錯誤,如果有錯誤,則跳轉!
if(1) longjmp(mark, 1);
}

void Func2()
{
// 其它代碼的執行
// 判斷程序遠行中,是否出現錯誤,如果有錯誤,則跳轉!
if(2) longjmp(mark, 2);
}

void Func3()
{
// 其它代碼的執行
// 判斷程序遠行中,是否出現錯誤,如果有錯誤,則跳轉!
if(-1) longjmp(mark, -1);
}

void main( void )
{
int jmpret;

jmpret = setjmp( mark );
if( jmpret == 0 )
{
// 其它代碼的執行

// 下面的這些函數執行過程中,有可能出現異常
Func1();

Func2();

Func3();

// 其它代碼的執行
}
else
{
// 錯誤處理模塊
switch (jmpret)
{
case 1:
printf( "Error 1\n");
break;
case 2:
printf( "Error 2\n");
break;
case 3:
printf( "Error 3\n");
break;
default :
printf( "Unknown Error");
break;
}
exit(0);
}

return;
}

  回顧一下,這與C++中提供的異常處理模型是不是很相近。異常的傳遞是可以跨越一個或多個函數。這的確為C程序員提供了一種較完善的異常處理編程的機制或手段。

setjmp和longjmp使用時,需要特別注意的事情

  1、setjmp與longjmp結合使用時,它們必須有嚴格的先後執行順序,也即先調用setjmp函數,之後再調用longjmp函數,以恢復到先前被保存的“程序執行點”。否則,如果在setjmp調用之前,執行longjmp函數,將導致程序的執行流變的不可預測,很容易導致程序崩潰而退出。請看示例程序,代碼如下:

class Test
{
public:
Test() {printf("構造對象\n");}
~Test() {printf("析構對象\n");}
}obj;

//注意,上面聲明了一個全局變量obj

void main( void )
{
int jmpret;

// 注意,這裡將會導致程序崩潰,無條件退出
Func1();
while(1);

jmpret = setjmp( mark );
if( jmpret == 0 )
{
// 其它代碼的執行

// 下面的這些函數執行過程中,有可能出現異常
Func1();

Func2();

Func3();

// 其它代碼的執行
}
else
{
// 錯誤處理模塊
switch (jmpret)
{
case 1:
printf( "Error 1\n");
break;
case 2:
printf( "Error 2\n");  

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved