6. 非局部跳转
(1)setjmp和longjmp语句
头文件 |
#include<setjmp.h> |
函数 |
int* setjmp(jum_buf env); |
返回值 |
直接调用返回0,若从longjmp调用返回则返回非0值 |
功能 |
设置非局部跳转的跳转点 |
|
|
函数 |
void longjmp(jmp_buf env, int val); |
功能 |
进行非局部转转,val为返回值 |
参数 |
env:一个特殊类型jmp_buf。这一数据类型是某种形式的数组,其中存放在调用longjmp时能用来恢复栈状态的信息(如,各寄存器的值;但不保存线程栈局部变量等数据)。一般,env变量是个全局变量,因为需要从另一个函数引用它。 |
备注 |
(1)C程序缺乏异常处理方法,可使用非局部跳转处理C程序的异常。 (2)goto语句仅限于函数内部跳转,而longjmp不限于。 |
【编程实验】非局部跳转
//process_jmp.c
#include <setjmp.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define TOK_ADD 5 #define TOK_SUB 6 char* const prompt = "cal: "; //命令行提示符 jmp_buf env; //保存运行环境,非局部跳转用于函数间的跳转 //命令行格式如:add 3 5 或 sub 5 3 void do_line(char* line); //解析并执行命令行内容 void cmd_add(void); void cmd_sub(void); int get_token(char* line); //从命令行参数中获取标识符 int main(int argc, char* argv[]) { ssize_t size = strlen(prompt) * sizeof(char); char buff[256]; ssize_t len = 0; //设置跳转点 if(setjmp(env) < 0){ //直接调用返回0,从longjmp跳转来时,返回longjmp中指定的val值 //这里我们设定val为正值,小于0表示setjmp调用失败! perror("setjmp error"); exit(1); } //输出提示符 write(STDOUT_FILENO, prompt, size); while(1) { len = read(STDIN_FILENO, buff, 256); if(len < 0) break; buff[len -1] = 0; do_line(buff); write(STDOUT_FILENO, prompt, size); } return 0; } void do_line(char* line) { int cmd = get_token(line); switch(cmd) { case TOK_ADD: cmd_add(); break; case TOK_SUB: cmd_sub(); break; default: fprintf(stderr, "error command\n"); } } void cmd_add(void) { int i = get_token(NULL); int j = get_token(NULL); printf("result: %d + %d = %d\n", i, j, i + j); } void cmd_sub(void) { int i = get_token(NULL); int j = get_token(NULL); printf("result: %d - %d = %d\n", i, j, i - j); } static int is_number(char* item) { int ret = 1; int len = strlen(item); int i = 0; for(i=0; i<len; i++) { ret = ret && ('0'<= item[i]) && (item[i] <= '9'); if (ret == 0) break; } return ret; } int get_token(char* line) { //格式add 3 4 // sub 7 5 char* item = strtok(line, " "); if(line != NULL) { if(!strcmp("add", item)) return TOK_ADD; if(!strcmp("sub", item)) return TOK_SUB; }else{ if(is_number(item)){ int i = atoi(item); return i; }else{ fprintf(stderr, "arg not number\n"); longjmp(env, 1); //1表示跳到跳转点(setjmp)后,setjmp的返回值 } } }
(2)longjmp对各类变量的影响
变量类型 |
受影响情况 |
全局变量、静态变量和volatile变量 |
不能恢复到原始值 |
寄存器变量 |
可以恢复到原始值 |
自动变量 |
优化编译后可能会恢复 |
①存放在存储器中的变量将具有longjmp时的值,而在cpu和浮点寄存器中的变量则恢复为调用setjmp时的值。
②不进行优化时,自动变量、寄存器变量、全局变量、静态变量和易失变量等都存放在存储器中(亦即忽略了对reg_var变量的register存储类说明)。
③而进行了优化后,auto_var和reg_var都存放在寄存器中(即使auto_var并未声明为register),volatile变量则仍存放在存储器中。
④全局、静态和易失变量不受优化的影响,在调用longjmp后,它们的值是最近所呈现的值。
⑤如果要编写一个使用非局部跳转的可移植程序,则必须使用volatile属性。
【编程实验】longjmp对变量的影响
//longjmp_var.c
#include <setjmp.h> #include <stdio.h> #include <stdlib.h> #include <malloc.h> int global_var; jmp_buf env; int show(int g_v, int s_v, int a_v, int r_v, int m_v, int v_v); int f1(int g_v, int s_v, int a_v, int r_v, int m_v, int v_v); void f2(); int main(int argc, char* argv[]) { static int sta_var; int auto_var; register reg_var; int* heap_var = (int*)malloc(sizeof(int)); volatile int vola_var;//易失变量 global_var = 1; sta_var = 2; auto_var = 3; reg_var = 4; *heap_var = 5; vola_var = 6; int k = 0; if((k = setjmp(env)) < 0 ){ perror("setjmp error"); }else if(k == 1){ printf("after longjmp:\n"); show(global_var, sta_var, auto_var, reg_var, *heap_var, vola_var); exit(0); } global_var = 10; sta_var = 20; auto_var = 30; reg_var = 40; *heap_var = 50; vola_var = 60; printf("before longjmp:\n"); f1(global_var, sta_var, auto_var, reg_var, *heap_var, vola_var); return 0; } int show(int g_v, int s_v, int a_v, int r_v, int m_v, int v_v) { printf(" global: %d, static: %d, auto: %d, reg: %d, heap: %d vola: %d\n", g_v, s_v, a_v, r_v, m_v, v_v); } int f1(int g_v, int s_v, int a_v, int r_v, int m_v, int v_v) { show(g_v, s_v, a_v, r_v, m_v, v_v); f2(); } void f2() { longjmp(env, 1); } /*输出结果: [root@bogon 5.process]# gcc -o bin/longjmp_var src/longjmp_var.c [root@bogon 5.process]# bin/longjmp_var before longjmp: global: 10, static: 20, auto: 30, reg: 40, heap: 50 vola: 60 after longjmp: global: 10, static: 20, auto: 30, reg: 40, heap: 50 vola: 60 [root@bogon 5.process]# gcc -O -o bin/longjmp_var src/longjmp_var.c [root@bogon 5.process]# bin/longjmp_var before longjmp: global: 10, static: 20, auto: 30, reg: 40, heap: 50 vola: 60 after longjmp: global: 10, static: 20, auto: 3, reg: 4, heap: 50 vola: 60 */