20.1 setjmp 和 longjmp 函数

20.1.1 函数介绍

  1. #include <setjmp.h>
  2. int setjmp(jmp_buf env);
  • 函数功能:设置非局部跳转的跳转点(设置跳转点)
  • 返回值:直接调用返回0,若从 longjmp 调用返回则返回0
  • 这个函数会被执行两次,一次是自己本身使用的时候返回0,另一次再调用 longjump 的时候,此函数再返回 longjmp 中的 val 值
  1. #include <setjmp.h>
  2. void longjmp(jmp_buf env, int val);
  • 函数功能:进行非局部跳转,val 为返回值(具体完成跳转,例如goto)
  • 参数:
    • @env:

      • 一个特殊类型 jmp_buf。这一数据类型是某种形式的数组,其中存放在调用 longjmp 时能用来恢复栈状态的所有信息。一般,env 变量是个全局变量,因为需从另一个函数中引用他。
  • C程序缺乏异常处理的语法,可使用非局部跳转处理C程序的异常
  • goto语句仅限于函数内部的跳转,而 longjmp 不限于

20.1.2 例子

  process_jmp.c

  1. #include <setjmp.h>
  2. #include <unistd.h>
  3. #include <string.h>
  4. #include <stdio.h>
  5. #include <stdlib.h>
  6.  
  7. #define TOK_ADD 5
  8. #define TOK_SUB 6
  9.  
  10. void do_line(char *line);
  11. void cmd_add(void);
  12. void cmd_sub(void);
  13. int get_token(char *item);/* 获取分割字符 */
  14.  
  15. char *prompt = "cal:"; /* 命令行提示符 */
  16. jmp_buf env;/* 跳转的 buf 结构 */
  17.  
  18. int main(void)
  19. {
  20. ssize_t size = strlen(prompt) * sizeof(char);
  21. char buff[];
  22. ssize_t len;
  23.  
  24. /* 设置跳转点 */
  25. /* setjmp 第一次执行成功返回0,调用 longjmp 后此处再返回 非0值 */
  26. 26 if(setjmp(env) < 0) {
  27. 27 perror("setjmp error");
  28. 28 exit(1);
  29. 29 }

  30. write(STDOUT_FILENO, prompt, size);
  31. while() {
  32. len = read(STDIN_FILENO, buff, );
  33. if(len < ) break;
  34.  
  35. buff[len - ] = ;
  36. do_line(buff);
  37. write(STDOUT_FILENO, prompt, size);
  38. }
  39.  
  40. return ;
  41. }
  42.  
  43. void do_line(char *line)
  44. {
  45. int cmd = get_token(line);
  46.  
  47. switch(cmd) {
  48. case TOK_ADD:
  49. cmd_add();
  50. break;
  51. case TOK_SUB:
  52. cmd_sub();
  53. break;
  54. default:
  55. fprintf(stderr, "error command\n");
  56. }
  57.  
  58. }
  59.  
  60. void cmd_add(void)
  61. {
  62. int i = get_token(NULL);
  63. int j = get_token(NULL);
  64. printf("result is %d\n", i + j);
  65. }
  66.  
  67. void cmd_sub(void)
  68. {
  69. int i = get_token(NULL);
  70. int j = get_token(NULL);
  71. printf("result is %d\n", i - j);
  72. }
  73.  
  74. static int is_number(char *item)
  75. {
  76. int len = strlen(item);
  77. int i;
  78.  
  79. for(i = ; i < len; i++)
  80. {
  81. if(item[i] > '' || item[i] < '')
  82. return ;
  83. }
  84.  
  85. return ;
  86. }
  87.  
  88. int get_token(char *line)
  89. {
  90. /*
  91. * add 3 4
  92. */
  93. char *item = strtok(line, " ");
  94.  
  95. if(line != NULL) {
  96. if(!strcmp("add", item)) return TOK_ADD;
  97. if(!strcmp("sub", item)) return TOK_SUB;
  98. } else {
  99. if(is_number(item)) {
  100. int i = atoi(item);
  101. return i;
  102. } else {
  103. fprintf(stderr, "arg not number\n");
  104. /* 如果输入的参数不正常,则让程序跳回到主函数执行下一次循环 */
  105. /* 进行非局部跳转 */
  106. longjmp(env, 1);// 跳转到 setjmp 处执行
  107. }
  108. }
  109. }

  执行成功

  

  如果将红色部分注释掉,会发现打印 reasult is xx 数字,xx数字 是一个随机值,因为再 get_token 函数中,fprintf 后就没有做退出也没有做返回实际数字,那么函数运行完毕后,就会返回一个随机值来做加减运行,结果也就变为了一个随机值。

20.2 非局部跳转中,变量的使用

  编译器优化编译后:

  • 全局变量、静态变量和 volatile(易矢变量)

    • 不能恢复到原始值
  • 寄存器变量
    • 可以恢复到原始值  
  • 自动变量潜在问题  
    • 优化编译后可能会恢复
  • malloc 变量
    • 与编译器优化有关,有的编译器进行优化编译会改变,有的编译器不会,具体看编译器优化    

  longjmp_val.c

  1. #include <setjmp.h>
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <malloc.h>
  5.  
  6. int g_val;
  7.  
  8. jmp_buf env;
  9.  
  10. /*
  11. * g_val:全局变量
  12. * s_val:静态变量
  13. * a_val:自动变量,即局部变量
  14. * r_val:寄存器变量
  15. * m_val:通过 malloc 分配的变量
  16. * v_val:易失变量
  17. */
  18. void fun1(int g_val, int s_val, int a_val, int r_val, int m_val, int v_val);
  19.  
  20. void fun2();
  21.  
  22. int main(void)
  23. {
  24. static int s_val;
  25. int a_val;
  26. register r_val;
  27. int *m_val = (int *)malloc(sizeof(int));
  28. volatile int v_val;
  29.  
  30. g_val = ;
  31. s_val = ;
  32. a_val = ;
  33. r_val = ;
  34. *m_val = ;
  35. v_val = ;
  36.  
  37. int k = ;
  38.  
  39. if((k = setjmp(env)) < ) {
  40. perror("setjmp error");
  41. exit();
  42. } else if( k == ) {
  43. printf("after longjmp\n");
  44. printf("g_val %d, s_val %d, a_val %d r_val %d, m_val %d v_val %d\n",
  45. g_val, s_val, a_val, r_val, *m_val, v_val);
  46. exit();
  47. }
  48.  
  49. g_val = ;
  50. s_val = ;
  51. a_val = ;
  52. r_val = ;
  53. *m_val = ;
  54. v_val = ;
  55.  
  56. fun1(g_val, s_val, a_val, r_val, *m_val, v_val);
  57.  
  58. return ;
  59. }
  60.  
  61. void fun1(int g_val, int s_val, int a_val, int r_val, int m_val, int v_val)
  62. {
  63. printf("before longjmp\n");
  64.  
  65. printf("g_val %d, s_val %d, a_val %d r_val %d, m_val %d v_val %d\n",
  66. g_val, s_val, a_val, r_val, m_val, v_val);
  67.  
  68. fun2();
  69. }
  70.  
  71. void fun2()
  72. {
  73. longjmp(env, );
  74. }

  不进行优化编译后执行:

  

  

  优化编译后:

  

  

二十、Linux 进程与信号---非局部跳转的更多相关文章

  1. 三十二、Linux 进程与信号——不可靠信号

    32.1 不可靠信号问题 发生信号时关联动作被重置为默认设置 信号可能丢失 程序片段 在进入 sig_int 与再次调用 signal 之间发生的 SIGINT 信号将不会捕获 导致进程终止 以前版本 ...

  2. 二十二、Linux 进程与信号---进程创建

    22.1 fork 和 vfork 函数 22.1.1 函数说明 #include <unistd.h> #include <sys/types.h> pid_t fork( ...

  3. 二十二、Linux 进程与信号---进程创建(续)

    22.2 父子进程操作文件 文件操作由两种模式: IO 系统调用操作文件 标准C IO 操作文件 看代码: #include <unistd.h> #include <string. ...

  4. Unix系统编程()执行非局部跳转:setjmp和longjmp

    使用库函数setjmp和longjmp可执行非局部跳转(local goto). 术语"非局部(nonlocal)"是指跳转目标为当前执行函数之外的某个位置. C语言里面有个&qu ...

  5. 【转】浅析C语言的非局部跳转:setjmp和longjmp

    转自 http://www.cnblogs.com/lienhua34/archive/2012/04/22/2464859.html C语言中有一个goto语句,其可以结合标号实现函数内部的任意跳转 ...

  6. 三十、Linux 进程与信号——信号的概念及 signal 函数

    30.1 信号的基本概念 信号(signal)机制是Linux 系统中最为古老的进程之间的通信机制,解决进程在正常运行过程中被中断的问题,导致进程的处理流程会发生变化 信号是软件中断 信号是异步事件 ...

  7. 十八、Linux 进程与信号---进程介绍

    18.1 进程的概念 程序:程序(program)是存放再磁盘文件中的可执行文件 进程 程序的执行实例被称为进程(process) 一个程序的执行实例可能由多个 进程具有独立的权限和职责.如果系统中某 ...

  8. 三十四、Linux 进程与信号——信号特点、信号集和信号屏蔽函数

    34.1 信号特点 信号的发生是随机的,但信号在何种条件下发生是可预测的 进程杠开始启动时,所有信号的处理方式要么默认,要么忽略:忽略是 SIGUSR1 和 SIGUSR2 两个信号,其他都采取默认方 ...

  9. 二十三、Linux 进程与信号---进程链和进程扇、守护进程和孤儿进程以及僵尸进程

    23.1 进程链和进程扇 23.1.1 概念 进程链:一个父进程构建出一个子进程,子进程再构建出子子进程,子子进程构建出子子子进程.... 这种就为进程链 进程扇:一个父进程构建出多个子进程,子进程都 ...

随机推荐

  1. 【转】WEB服务器与应用服务器的区别

    https://blog.csdn.net/liupeng900605/article/details/7661406 一.简述 WEB服务器与应用服务器的区别: 1.WEB服务器: 理解WEB服务器 ...

  2. cf1088E Ehab and a component choosing problem (树形dp)

    题意(考试时看错了对着样例wa了好久..):从树上选k个连通块,使得权值的平均值最大的基础上,选的块数最多 如果不考虑块数最多的限制,肯定是只选一个权值最大的块是最好的 然后只要看这个权值最大的块有多 ...

  3. bzoj1066 蜥蜴 (dinic)

    最大流板子题. 对于每根柱子,建两个点ai,bi,建边(ai,bi,柱子高度) 对于距离不超过d的两根柱子i,j,建边(bi,aj,inf) 对于起始位置在i的每个蜥蜴,建边(S,ai,1) 对于能跳 ...

  4. P1966 火柴排队

    这道题需要小小的思考一波 (然而我思考了两节课) 好,我们先得出一个结论:a中第k大的与b中第k大的一定要排在一起,才能保证最小. 然后发现:挪a,b其实没有区别,故我们固定a,挪b. 然后我们就思考 ...

  5. [luogu1020][导弹拦截]

    题目位置 https://www.luogu.org/problemnew/show/P1020 题目描述 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统.但是这种导弹拦截系统有一个缺陷:虽然它的 ...

  6. TestNg 8.参数化测试 - xml

    有的时候,case需要参数,那么,传餐怎么传? 我的目录结构:首先建一个包叫做parameter,然后在resource里面新建一个parameter.xml文件 看以下代码: ParameterTe ...

  7. 1. github配置

    1. 安装:官网傻瓜式安装 2.密钥的生成:为了不让不想干的人提交代码,所以需要一个密钥 执行这个命令 : ssh-keygen -t rsa -C "邮箱地址" 然后一直回车键回 ...

  8. operator new和operator delete

    从STL源码剖析中看到了operator new的使用 template<class T> inline void _deallocate(T* buffer) { ::operator ...

  9. POJ-1077 HDU 1043 HDU 3567 Eight (BFS预处理+康拓展开)

    思路: 这三个题是一个比一个令人纠结呀. POJ-1077 爆搜可以过,94ms,注意不能用map就是了. #include<iostream> #include<stack> ...

  10. JDBC查询MySQL中的表

    在数据库test里先创建表school,内容如下 创建接口对象:Statement stmt=con.createStatement(); //创建语句(Statement)ResultSet res ...