setjmp與longjmp結合使用時,它們必須有嚴格的先后執行順序,也即先調用setjmp函數,之后再調用longjmp函數,以恢復到先前被保存的“程序執行點”。(從哪兒來,回哪兒去)否則,如果在setjmp調用之前,執行longjmp函數,將導致程序的執行流變的不可預測,很容易導致程序崩潰而退出。
1.人們對于goto語句的忌諱,很多的專業書籍以及專業人士號召限制goto語句的使用,此時,setjmp與longjmp對goto語句有了很好的替代作用.
2.goto語句有一個局限性,它只能在函數內部跳轉.而setjmp與longjmp可以在整個程序全局中跳轉,實現"長跳轉",彌補了goto功能的局限.
3.使用setjmp和longjmp可以捕捉程序中的異常,并采取異常處理機制.
一個例子? 使用setjmp,longjmp處理異常.
? 2 #include<setjmp.h>
? 3 jmp_buf jumper;
? 4 void exception();
? 5 int deal_exception();
? 6
? 7 main()
? 8 {
? 9???????? int value;
?10???????? int i=0;
?11
?12???????? value=setjmp(jumper);
?13???????? if(0==value){
?14???????????????? exception();
?15???????? }
?16???????? else{
?17???????????????? switch(value)
?18???????????????? {
?19???????????????????????? case 1:
?20???????????????????????????????? printf("解決異常情況[%d]\n",value);
?21???????????????????????????????? break;
?22???????????????????????? case 2:
?23????????????????????????????????? printf("解決異常情況[%d]\n",value);
?24????????????????????????????????? break;
?25???????????????????????? case 3:
?26????????????????????????????????? printf("解決異常情況[%d]\n",value);
?27????????????????????????????????? break;
?28
?29???????????????????????? default:
?30
?31????????????????????????????????? break;
?32???????????????? }
?33???????? }
?34 }
?35 void exception()
?36 {
?37???????? int _err_no;
?38???????? if(_err_no=3){
?39???????????????? printf("出現異常情況[%d]\n",_err_no);
?40???????????????? longjmp(jumper,_err_no);
?41???????? }
?42???????? return;
?43 }
結果:
[root@localhost test]# gcc error.c
[root@localhost test]# ./a.out
出現異常情況[3]
解決異常情況[3]
[root@localhost test]#
C語言的try and catch機制
C語言具有的類似try and catch機制,unix下機制也相同。
VC例子:
#include <stdio.h>
#include <setjmp.h>
jmp_buf mark;? // 保存stack環境的變量
?
void main(void)
{
int jmpret;
int result;
jmpret = setjmp( mark );? @1@ // 保存當前stack環境到mark變量
if( jmpret == 0 )
{
scanf("%d",&result);
if(result<0) {
longjmp( mark, -1 );? // 發生錯誤,恢復保存的stack環境,執行跳轉到@1@處,jmpret的值為-1
return;
???? // 此語句永遠不被執行
} else {
printf("Normal!");
return;
}
} else {
// 錯誤處理
printf("An exception occured!");
}
}
?
?
longjmp在Standard C中的說明:
1.用longjmp跳轉時保證靜態變量有正確的值
2.auto變量只保證擁有volatile類型和在setjmp和longjmp之間沒有改變的值。
解釋:
1.靜態變量保持為在longjmp調用時的值
2.在setjmp和longjmp之間沒有改變的值仍然是原值,(很簡單,因為從來就沒有改變過)
對于一些auto-nonvolatile的值,可能在循環中,編譯器為了優化性能,用寄存器來緩存,longjmp跳轉時沒有賦值,用volatile修飾能阻止編譯器優化。
這兩個函數都要包含頭文件setjmp.h。而且它們在處理出現在深層函數嵌套的錯誤情況時很有用處。??? setjmp這個函數很有意思,雖然是一個函數,可是卻可以返回兩個不同的值。當第一次直接調用setjmp時,返回值為0。當從longjmp函數返回時,setjmp函數的返回值為longjmp的第二個參數的值。
??? 那么在什么地方調用setjmp呢?我們希望當從longjmp函數返回時,程序從哪里接著開始運行,我們就在哪里調用setjmp。看個小實例,你就明白是怎么回事了。
??? #include<stdio.h>
??? #include<setjmp.h>
??? jmp_buf ebuf;
??? void f2(void);
?? int main(void)
?? {
???? int i;
???? printf("1");
???? i=setjmp(ebuf);
???? if(i==0)????????????????????????????????????????????? //第一次執行到這里時,值為0,所以接下來執行f2()
???? {
?? f2();
?? printf("This will not be printed.");
???? }
???? printf("%d",i);?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? //由于從longjmp返回時,i=3,不執行if,所以執行該行
???? return 0;
}
?? void f2(void)
{
?? printf("2");?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ??
?? longjmp(ebuf,3);?? ?? ?? ?? ?? ?? ?? //longjmp函數返回,回到setjmp的位置,使得setjmp返回值為3
}
??? 函數最后的執行結果為123,嘻嘻。
????? longjmp注意:
1.不要假象寄存器類型的變量將總會保持不變。在調用longjmp之后,通過setjmp所返回的控制流中,例程中寄存器類型的變量將不會被恢復。
2.不要使用longjmp函數來實現把控制流,從一個中斷處理例程中傳出,除非被捕獲的異常是一個浮點數異常。在后一種情況下,如果程序通過調用 _fpreset函數,來首先初始化浮點數包后,它是可以通過longjmp來實現從中斷處理例程中返回。
3. 在C++程序中,小心對setjmp和longjmp的使用,應為setjmp和longjmp并不能很好地支持C++中面向對象的語義。因此在C++程序中,使用C++提供的異常處理機制將會更加安全。把setjmp和longjmp組合起來,原來它這么厲害
使用setjmp時必須使用頭文件setjmp.h。
#include "setjmp.h"
jmp_buf jmpbuffer;
?
int setjmp(jmp_buf jmpbuffer);
void longjmp(jmp_buf jumpbuffer, int retval);
?
其中 jmpbuffer 是相關的程序棧的環境上下文。
初始化jmpbuffer之后, setjmp第一次調用的時候會返回 0。
longjmp跳轉到setjmp處,其中第二個參數retval就是傳遞給setjmp, 作為setjmp的返回值。但是需要主要的是,如果retval設置為0, 即這樣調用的時候 longjmp(jumpbuffer, 0), setjmp會返回1。