函数间跳转

2014-07-31 18:16:57   最后更新: 2014-07-31 18:16:57   访问数量:804




在C语言中,goto语句是不能跨越函数的,而执行这类功能的函数就是 setjmp 和 longjmp,这两个函数对于处理深层嵌套调用中的出错情况是非常有用的

比如一个程序中main函数循环调用fgets读取,然后用do_line函数循环处理读到的每一行,在do_line函数中再循环调用cmd_add,在cmd_add函数中又循环调用get_line函数,这样的程序中,如果在某一层遇到错误返回,又不想停止处理,只是想跳过当前行继续执行,正常情况下,需要根据每个函数的返回值判断之后逐层返回,这是非常麻烦的

解决这样问题的方法就是用setjmp和longjmp函数

 

int setjmp (jmp_buf env);

 

定义于 setjmp.h头文件中,若直接调用返回0,若从longjmp调用返回longjmp函数的第二个参数

 

void longjmp (jmp_buf env, int val);

 

定义于setjmp.h头文件中,第二个参数为非0值,用于返回给setjmp函数处理

 

jmp_buf是一个数组,用于存储用来恢复栈状态的所有信息,因为要在另一个函数中引用env,所以规范的做法是将env变量定义为全局变量。

 

e.g.

/* * file: jmp.c * author: 龙泉居士 * date: 2012-09-10 */ #include "../apue.2e/include/apue.h" #include <setjmp.h> #define TOK_ADD -5 void do_line (char *); void cmd_add (void); int get_tocken (void); jmp_buf jmpbuffer; int main () { char line[MAXLINE]; if (setjmp(jmpbuffer) == 1) printf ("error\n"); while (fgets (line, MAXLINE, stdin) != NULL) do_line (line); exit (0); } char *tok_ptr; void do_line (char *ptr) { int cmd; tok_ptr = ptr; while ((cmd = get_tocken()) < 0) { switch (cmd) { case TOK_ADD: cmd_add(); break; } } } void cmd_add (void) { int tocken; tocken = get_tocken(); if (tocken < 0) longjmp (jmpbuffer, 1); } int get_tocken () { return -5; }

 

程序总会在52行调用后直接跳回到第22行的setjmp的地方

 

要注意的一点是回滚后的各个变量的值是不确定的,大多数实现并不回滚自动变量和寄存器变量的值

如果你有一个自动变量,而又不想让其回滚,可以定义为 volatile 属性或者声明为全局或静态变量

 

e.g.

/* * file: jmp.c * author: 龙泉居士 * date: 2012-09-10 18:03 */ #include "../apue.2e/include/apue.h" #include <setjmp.h> static void f1 (int, int, int, int); static void f2 (void); static jmp_buf jmpbuffer; static int globval; int main () { int autoval; register int regival; volatile int volaval; static int statval; globval = 1; autoval = 2; regival = 3; volaval = 4; statval = 5; if (setjmp(jmpbuffer) != 0) { printf ("after longjmp:\n"); printf ("globval = %d\nautoval = %d\nregival = %d\nvolaval = %d\nstatval = %d\n", globval, autoval, regival, volaval, statval); exit(0); } globval = 95; autoval = 96; regival = 97; volaval = 98; statval = 99; f1 (autoval, regival, volaval, statval); exit (0); } static void f1(int i, int j, int k, int l) { printf ("in f1() :\n"); printf ("globval = %d\nautoval = %d\nregival = %d\nvolaval = %d\nstatval = %d\n", globval, i, j, k, l); f2 (); } static void f2 (void) { longjmp (jmpbuffer, 1); }

 

 

当使用 cc jmp.c 编译后,运行结果是:

in f1() :

globval = 95

autoval = 96

regival = 97

volaval = 98

statval = 99

after longjmp:

globval = 95

autoval = 96

regival = 97

volaval = 98

statval = 99

 

当使用 cc -O jmp.c (即让编译器主动优化代码后编译)编译后,运行结果是:

in f1() :

globval = 95

autoval = 96

regival = 97

volaval = 98

statval = 99

after longjmp:

globval = 95

autoval = 2

regival = 3

volaval = 98

statval = 99

 

从结果可以看出来,我们不能保证auto变量和register变量的值,因为对于编译器,它可以选择跳过其中的不必要的代码执行。但是volatile变量、全局变量和静态变量的值我们是可以保证的。






读书笔记      技术帖      linux      unix      c语言      龙潭书斋      apue      unix环境高级编程      goto      setjmp      longjmp     


京ICP备15018585号