C语言中的longjump和setjump函数

电子说

1.3w人已加入

描述

  相信大家看到这个标题,可能已经猜到本文要谈的话题了。没错,今天给大家介绍一种比goto还要“任性”的跳转方式,那就是C函数库中的如下两个函数:

	

1//所需头文件 2#include  3 4int setjump(jmp_buf buf) 5void longjump(jmp_buf buf, int i)  6

  一些朋友该说了,“我从来不用这些跳转,免得出问题”。还是一直以来的那句话,“存在即合理”~

  下面,我们来看看这两个函数到底有什么可以推敲的东西。

  1函数介绍

  有研究过RTOS的朋友应该对此不难理解,setjump主要是保存当前函数调用点的现场环境(或者叫上下文),比如各种寄存器、堆栈等等,那么这些环境信息就记录在jmp_buf所定义的buf中。

  而当我们在其他位置调用longjump函数就相当于一个长跳转,传入之前保存在buf中的信息,即可跳回到之前setjump所调用的位置(理解为恢复setjump所保存的环境也是可以的)。

  所以,这里值得注意的是,不要率先调用longjump,否则程序不知道飞去哪里了。

  其实跟RTOS中进行任务切换有着异曲同工之妙。

  你大概已经注意到setjump有一个返回值,其主要分为两种情况:

  当直接调用setjump函数,则返回0;

  当调用longjump跳转到setjump位置,则其返回longjump的第二个非零参数。

  2跟goto有啥区别?

  以前我也跟大家介绍过goto这匹野马被驯服的方式(goto关键字你不知道的"那些事"(C语言提升)),在C语言中goto只能实现函数内部的跳转,无法实现跨函数的直接跳转,比如函数嵌套多层的跳转等等。

  当然,你也可以借助goto与函数返回配合完成函数之间的跳转,不过那太麻烦了,所以这两个库函数该派上用场了。

  这样的跳转太过于霸道,我们还是限制一下,切不可滥用,但其为异常处理代码的模块化带来了福音,在非常多的开源库中都有实际应用。

  下面给大家一个参考示例 ::


	

1#include  2#include  3 4jmp_buf mark; 5int  fperr; 6void fpcheck(void); 7 8/********************************************* 9 * Function: main 10 * Description : 主任务函数 11 * Note:(公众号:最后一个bug) 12 *********************************************/ 13int  main( void ) 14{ 15    int jmpret; 16 17    //记录异常代码与正常代码分支位置 18    jmpret = setjmp(mark); 19    if( jmpret == 0 ) 20    { 21        //正常用户程序运行 22 23    } 24    else 25    { 26        //在正常用户程序运行过程中发生异常 27        fpcheck();   28    } 29} 30/********************************************* 31 * Function: Errorhandler 32 * Description : 异常中断,在正常用户程序运行过程中发生异常处理函数 33 * Note:(公众号:最后一个bug) 34 *********************************************/ 35void Errorhandler(void) 36{ 37    fperr = num; 38    longjmp( mark, -1 ); //进行长跳转到异常处理 39} 40 41/********************************************* 42 * Function: fpcheck 43 * Description : 故障处理函数 44 * Note:(公众号:最后一个bug) 45 *********************************************/ 46void fpcheck(void) 47{ 48 49    switch( fperr ) 50    { 51        case INVALID: 52            //user Code  53            break; 54 55        case OVERFLOW: 56            //user Code  57            break; 58 59        case ZERODIVIDE: 60            //user Code  61            break; 62        default: 63            break; 64    } 65 66}

  3局限性

  这组函数除了前面介绍的注意事项,还有一个非常值得注意的点就是longjump的调用时机必须在setjump被调用的所在函数返回前。

  因为setjump保存有堆栈信息等,一旦setjump的被调用的函数返回则相应的环境会被释放,导致longjump无法在恢复到setjump调用位置,可能造成程序奔溃。

  最后

  好了,今天就跟大家分享这么多了,这一块还有一些东西可以挖掘,后面再整理一下分享出来。如果你觉得有所收获,一定记得点个赞!

  原文标题:C语言中比goto还“霸道”的跳转方式

  文章出处:【微信公众号:嵌入式ARM】欢迎添加关注!文章转载请注明出处。

  审核编辑:汤梓红

 


打开APP阅读更多精彩内容
声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉

全部0条评论

快来发表一下你的评论吧 !

×
20
完善资料,
赚取积分