setjmp和longjmp函数使用详解

在网上看到的,觉得很有用,copy过来的。

非局部跳转语句---setjmp和longjmp函数。非局部指的是,这不是由普通C语言goto,语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。
#include <setjmp.h>
Int setjmp(jmp_buf  env);
       返回值:若直接调用则返回0,若从longjmp调用返回则返回非0值的longjmp中的val值
Void longjmp(jmp_buf env,int val);
        调用此函数则返回到语句setjmp所在的地方,其中env 就是setjmp中的 env,而val 则是使setjmp的返回值变为val。
        当检查到一个错误时,则以两个参数调用longjmp函数,第一个就是在调用setjmp时所用的env,第二个参数是具有非0值的val,它将成为从setjmp处返回的值。使用第二个参数的原因是对于一个setjmp可以有多个longjmp。

  1. #include <stdio.h>  
  2. #include <setjmp.h>  
  3.    
  4. static jmp_buf buf;  
  5.    
  6. void second(void) {  
  7.     printf("second\n");         // 打印  
  8.     longjmp(buf,1);             // 跳回setjmp的调用处 - 使得setjmp返回值为1  
  9. }  
  10.    
  11. void first(void) {  
  12.     second();  
  13.     printf("first\n");          // 不可能执行到此行  
  14. }  
  15.    
  16. int main() {     
  17.     if ( ! setjmp(buf) ) {  
  18.         first();                // 进入此行前,setjmp返回0  
  19.     } else {                    // 当longjmp跳转回,setjmp返回1,因此进入此行  
  20.         printf("main\n");       // 打印  
  21.     }  
  22.    
  23.     return 0;  
  24. }  


 

上述程序将输出:

second
main

注意到虽然first()子程序被调用,"first"不可能被打印。"main"被打印,因为条件语句if ( ! setjmp(buf) )被执行第二次。 

使用setjmp和longjmp要注意以下几点:

1、setjmp与longjmp结合使用时,它们必须有严格的先后执行顺序,也即先调用setjmp函数,之后再调用longjmp函数,以恢复到先前被保存的“程序执行点”。否则,如果在setjmp调用之前,执行longjmp函数,将导致程序的执行流变的不可预测,很容易导致程序崩溃而退出

2.  longjmp必须在setjmp调用之后,而且longjmp必须在setjmp的作用域之内。具体来说,在一个函数中使用setjmp来初始化一个全局标号,然后只要该函数未曾返回,那么在其它任何地方都可以通过longjmp调用来跳转到 setjmp的下一条语句执行。实际上setjmp函数将发生调用处的局部环境保存在了一个jmp_buf的结构当中,只要主调函数中对应的内存未曾释放 (函数返回时局部内存就失效了),那么在调用longjmp的时候就可以根据已保存的jmp_buf参数恢复到setjmp的地方执行。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

异常处理

在下例中,setjmp被用于包住一个例外处理,类似trylongjmp调用类似于throw语句,允许一个异常返回给setjmp一个异常值。下属代码示例遵从1999 ISO C standardSingle UNIX Specification:仅在特定范围内引用setjmp

  • ifswitch或它们的嵌套使用的条件表达式
  • 上述情况下与!一起使用或者与整数常值比较
  • 作为单独的语句(不使用其返回值)

遵从上述规则使得创建程序环境缓冲区更为容易。更一般的使用setjmp可能引起未定义行为,如破坏局部变量;编译器被要求保护或警告这些用法。但轻微的复杂用法如switch ((exception_type = setjmp(env))) { }在文献与实践中是常见的,并保持了相当的可移植性。

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4. #include <setjmp.h>  
  5.    
  6. void first(void);  
  7. void second(void);  
  8.    
  9. /* This program's output is: 
  10.   
  11. calling first 
  12. calling second 
  13. entering second 
  14. second failed with type 3 exception; remapping to type 1. 
  15. first failed, exception type 1 
  16.   
  17. */  
  18.    
  19. /* Use a file scoped static variable for the exception stack so we can access 
  20.  * it anywhere within this translation unit. */  
  21. static jmp_buf exception_env;  
  22. static int exception_type;  
  23.    
  24. int main() {  
  25.     void *volatile mem_buffer;  
  26.    
  27.     mem_buffer = NULL;  
  28.     if (setjmp(exception_env)) {  
  29.         /* if we get here there was an exception */  
  30.         printf("first failed, exception type %d\n", exception_type);  
  31.     } else {  
  32.         /* Run code that may signal failure via longjmp. */  
  33.         printf("calling first\n");  
  34.         first();  
  35.         mem_buffer = malloc(300); /* allocate a resource */  
  36.         printf(strcpy((char*) mem_buffer, "first succeeded!")); /* ... this will not happen */  
  37.     }  
  38.     if (mem_buffer)  
  39.         free((void*) mem_buffer); /* carefully deallocate resource */  
  40.     return 0;  
  41. }  
  42.    
  43. void first(void) {  
  44.     jmp_buf my_env;  
  45.    
  46.     printf("calling second\n");  
  47.     memcpy(my_env, exception_env, sizeof(jmp_buf));  
  48.     switch (setjmp(exception_env)) {  
  49.         case 3:  
  50.             /* if we get here there was an exception. */  
  51.             printf("second failed with type 3 exception; remapping to type 1.\n");  
  52.             exception_type = 1;  
  53.    
  54.         default/* fall through */  
  55.             memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */  
  56.             longjmp(exception_env, exception_type); /* continue handling the exception */  
  57.    
  58.         case 0:  
  59.             /* normal, desired operation */  
  60.             second();  
  61.             printf("second succeeded\n");  /* not reached */  
  62.     }  
  63.     memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */  
  64. }  
  65.    
  66. void second(void) {  
  67.     printf("entering second\n" ); /* reached */  
  68.     exception_type = 3;  
  69.     longjmp(exception_env, exception_type); /* declare that the program has failed */  
  70.     printf("leaving second\n"); /* not reached */  
  71. }  

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值