p(gJmpBuf, 1);
?5 }
?6 void Func2(){
?7 ? ? printf("Enter Func2\n");
?8 ? ? if(0)longjmp(gJmpBuf, 2);
?9 }
10 void Func3(){
11 ? ? printf("Enter Func3\n");
12 ? ? if(1)longjmp(gJmpBuf, 3);
13 }
14?
15 int main(void)
16 {
17 ? ? int dwJmpRet = setjmp(gJmpBuf);
18 ? ? printf("dwJmpRet = %d\n", dwJmpRet);
19 ? ? if(0 == dwJmpRet)
20 ? ? {
21 ? ? ? ? Func1();
22 ? ? ? ? Func2();
23 ? ? ? ? Func3();
24 ? ? }
25 ? ? else
26 ? ? {
27 ? ? ? ? switch(dwJmpRet)
28 ? ? ? ? {
29 ? ? ? ? ? ? case 1:
30 ? ? ? ? ? ? ? ? printf("Jump back from Func1\n");
31 ? ? ? ? ? ? break;
32 ? ? ? ? ? ? case 2:
33 ? ? ? ? ? ? ? ? printf("Jump back from Func2\n");
34 ? ? ? ? ? ? break;
35 ? ? ? ? ? ? case 3:
36 ? ? ? ? ? ? ? ? printf("Jump back from Func3\n");
37 ? ? ? ? ? ? break;
38 ? ? ? ? ? ? default:
39 ? ? ? ? ? ? ? ? printf("Unknown Func!\n");
40 ? ? ? ? ? ? break;
41 ? ? ? ? }
42 ? ? }
43 ? ? return 0;
44 }
复制代码
? ? ?执行结果为:
?
1 dwJmpRet = 0
2 Enter Func1
3 Enter Func2
4 Enter Func3
5 dwJmpRet = 3
6 Jump back from Func3
? ? ?当setjmp/longjmp嵌在单个函数中使用时,可模拟PASCAL语言中嵌套函数定义(即函数内中定义一个局部函数)。当setjmp/longjmp跨越函数使用时,可模拟面向对象语言中的异常(exception) 机制。
?
? ? 模拟异常异常机制时,首先通过setjmp()函数设置一个跳转点并保存返回现场,然后使用try块包含那些可能出现错误的代码。可在try块代码中或其调用的函数内,通过longjmp()函数抛出(throw)异常。抛出异常后,将跳回setjmp()函数所设置的跳转点并执行catch块所包含的异常处理程序中。
?
? ? ?以除零错误为例:
?
复制代码
?1 jmp_buf gJmpBuf;
?2 void RaiseException(void)
?3 {
?4 ? ?printf("Exception is raised: ");
?5 ? ?longjmp(gJmpBuf, 1); ?//throw,跳转至异常处理代码
?6 ? ?printf("This line should never get printed!\n");
?7 }
?8 double Division(double fDividend, double fDivisor)
?9 {
10 ? ? return fDividend/fDivisor;
11 }
12 int main(void)
13 {
14 ? ? double fDividend = 0.0, fDivisor = 0.0;
15 ? ? printf("Enter the dividend: ");
16 ? ? scanf("%lf", &fDividend);
17 ? ? printf("Enter the divisor : ");
18 ? ? if(0 == setjmp(gJmpBuf)) ?//try块
19 ? ? {
20 ? ? ? ? scanf("%lf", &fDivisor);
21 ? ? ? ? if(0 == fDivisor) //也可将该判断及RaiseException置于Division内
22 ? ? ? ? ? ? RaiseException();
23 ? ? ? ? printf("The quotient is %.2lf\n", Division(fDividend, fDivisor));
24 ? ? }
25 ? ? else ?//catch块(异常处理代码)
26 ? ? {
27 ? ? ? ? printf("The divisor cannot be 0!\n");
28 ? ? }
29?
30 ? ? return 0;
31 }
复制代码
? ? ?执行结果为:
?
1 Enter the dividend: 10
2 Enter the divisor : 0
3 Exception is raised: The divisor cannot be 0!
? ? ?通过组合使用setjmp/longjmp函数,可对复杂程序中可能出现的异常进行集中处理。根据longjmp()函数所传递的返回值来区分处理各种不同的异常。
?
? ? ?使用setjmp/longjmp函数时应注意以下几点:
?
? ? ?1) 必须先调用setjmp()函数后调用longjmp()函数,以恢复到先前被保存的程序执行点。若调用顺序相反,将导致程序的执行流变得不可预测,很容易导致程序崩溃。
?
? ? ?2) longjmp()函数必须在setjmp()函数的作用域之内。在调用setjmp()函数时,它保存的程序执行点环境只在当前主调函数作用域以内(或以后)有效。若主调函数返回或退出到上层(或更上层)的函数环境中,则setjmp()函数所保存的程序环境也随之失效(函数返回时堆栈内存失效)。这就要求setjmp()不可该封装在一个函数中,若要封装则必须使用宏(详见《c语言接口与实现》“第4章 异常与断言”)。
?
? ? ?3) 通常将jmp_buf变量定义为全局变量,以便跨函数调用longjmp。
?
? ? ?4) 通常,存放在存储器中的变量将具有longjmp时的值,而在CPU和浮点寄存器中的变量则恢复为调用setjmp时的值。因此,若在调用setjmp和longjmp之间修改自动变量或寄存器变量的值,当setjmp从longjmp调用返回时,变量将维持修改后的值。若要编写使用非局部跳转的可移植程序,必须使用volatile属性。
?
? ? ?5) 使用异常机制不必每次调用都检查一次返回值,但因为程序中任何位置都可能抛出异常,必须时刻考虑是否捕捉异常。在大型程序中,判断是否捕捉异常会是很大的思维负担,影响开发效率。相比之下,通过返回值指示错误有利于调用者在最近出错的地方进行检查。此外,返回值模式中程序的运行顺序一目了然,对维护者可读性更高。因此,应用程序中不建议使用setjmp/longjmp“异常处理”机制(除非库或框架)。
?
2.5 信号(signal/raise)
? ? ?在某些情况下,主机环