20.1 setjmp 和 longjmp 函数
20.1.1 函数介绍
#include <setjmp.h>
int setjmp(jmp_buf env);
- 函数功能:设置非局部跳转的跳转点(设置跳转点)
- 返回值:直接调用返回0,若从 longjmp 调用返回则返回0
- 这个函数会被执行两次,一次是自己本身使用的时候返回0,另一次再调用 longjump 的时候,此函数再返回 longjmp 中的 val 值
#include <setjmp.h> void longjmp(jmp_buf env, int val);
- 函数功能:进行非局部跳转,val 为返回值(具体完成跳转,例如goto)
- 参数:
- @env:
- 一个特殊类型 jmp_buf。这一数据类型是某种形式的数组,其中存放在调用 longjmp 时能用来恢复栈状态的所有信息。一般,env 变量是个全局变量,因为需从另一个函数中引用他。
- @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[256]; 22 ssize_t len; 23 24 /* 设置跳转点 */ 25 /* setjmp 第一次执行成功返回0,调用 longjmp 后此处再返回 非0值 */ 26 if(setjmp(env) < 0) { 27 perror("setjmp error"); 28 exit(1); 29 } 30 31 write(STDOUT_FILENO, prompt, size); 32 while(1) { 33 len = read(STDIN_FILENO, buff, 256); 34 if(len < 0) break; 35 36 buff[len - 1] = 0; 37 do_line(buff); 38 write(STDOUT_FILENO, prompt, size); 39 } 40 41 return 0; 42 } 43 44 void do_line(char *line) 45 { 46 int cmd = get_token(line); 47 48 switch(cmd) { 49 case TOK_ADD: 50 cmd_add(); 51 break; 52 case TOK_SUB: 53 cmd_sub(); 54 break; 55 default: 56 fprintf(stderr, "error command "); 57 } 58 59 } 60 61 void cmd_add(void) 62 { 63 int i = get_token(NULL); 64 int j = get_token(NULL); 65 printf("result is %d ", i + j); 66 } 67 68 void cmd_sub(void) 69 { 70 int i = get_token(NULL); 71 int j = get_token(NULL); 72 printf("result is %d ", i - j); 73 } 74 75 static int is_number(char *item) 76 { 77 int len = strlen(item); 78 int i; 79 80 for(i = 0; i < len; i++) 81 { 82 if(item[i] > '9' || item[i] < '0') 83 return 0; 84 } 85 86 return 1; 87 } 88 89 int get_token(char *line) 90 { 91 /* 92 * add 3 4 93 */ 94 char *item = strtok(line, " "); 95 96 if(line != NULL) { 97 if(!strcmp("add", item)) return TOK_ADD; 98 if(!strcmp("sub", item)) return TOK_SUB; 99 } else { 100 if(is_number(item)) { 101 int i = atoi(item); 102 return i; 103 } else { 104 fprintf(stderr, "arg not number "); 105 /* 如果输入的参数不正常,则让程序跳回到主函数执行下一次循环 */ 106 /* 进行非局部跳转 */ 107 longjmp(env, 1);// 跳转到 setjmp 处执行 108 } 109 } 110 }
执行成功
如果将红色部分注释掉,会发现打印 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 = 1; 31 s_val = 2; 32 a_val = 3; 33 r_val = 4; 34 *m_val = 5; 35 v_val = 6; 36 37 int k = 0; 38 39 if((k = setjmp(env)) < 0) { 40 perror("setjmp error"); 41 exit(1); 42 } else if( k == 1) { 43 printf("after longjmp "); 44 printf("g_val %d, s_val %d, a_val %d r_val %d, m_val %d v_val %d ", 45 g_val, s_val, a_val, r_val, *m_val, v_val); 46 exit(0); 47 } 48 49 g_val = 10; 50 s_val = 20; 51 a_val = 30; 52 r_val = 40; 53 *m_val = 50; 54 v_val = 60; 55 56 fun1(g_val, s_val, a_val, r_val, *m_val, v_val); 57 58 return 0; 59 } 60 61 62 void fun1(int g_val, int s_val, int a_val, int r_val, int m_val, int v_val) 63 { 64 printf("before longjmp "); 65 66 printf("g_val %d, s_val %d, a_val %d r_val %d, m_val %d v_val %d ", 67 g_val, s_val, a_val, r_val, m_val, v_val); 68 69 fun2(); 70 } 71 72 void fun2() 73 { 74 longjmp(env, 1); 75 }
不进行优化编译后执行:
优化编译后: