c语言中的函数间的跳转函数setjmp和longjmp

       原创文章,转载请注明出处,谢谢!       
       作者:清林,博客名:飞空静渡

 

我们知道,在c语言中我们可以使用goto语句在一个函数中进行跳转,例如一个常用的goto方式为:

 

......

//分配资源

......

 

if error1

   goto error;

 

if error2

   goto error;

 

........

error:

    ......

    // 释放资源

    ......

 

但是goto语句也只限于一个函数内,不能进行函数间的跳转。在c语言中,进行函数间的跳转使用setjmp和longjmp函数。

为什么要有个函数间的跳转呢,这是因为,如果我们在调用函数时,函数的调用的嵌套的层次很深的话,如果出错,那么一层一层的返回和判断就很麻烦,所以,如果出错,就可以直接返回到最上面的调用的函数就会很方便。

 

我们来看一下这两个函数怎么使用,首先看一下这两个函数的原型:

#include <setjmp.h>

 

int setjmp(jmp_buf env);   //直接调用则返回0,如从longjmp调用则返回非0

int longjmp(jmp_buf env, int val);

jum_buf是一个类型,其中env存储了一些longjmp调用返回用来恢复栈状态的所有信息。longjmp中的env和setjmp中的env是同一个。val是程序员自定义的直,这个值用在setjmp的返回值中,这样我们就可以知道是在哪个的longjmp跳转回来的。另外,由于在不同的函数间调用setjmp和longjmp,而这两个函数要公用一个env变量,所以把env定义为一个全局变量。

 

下面我们看一个简单的例子:

#include <stdio.h> #include <setjmp.h> jmp_buf jmpbuffer; void func1(); void func2(); int main(void) { printf("in main function!/n"); if(setjmp(jmpbuffer) != 0) { printf("get the error from jump return!/n"); return -1; } func1(); printf("main function end!/n"); return 0; } void func1() { printf("in func1 function!/n"); func2(); } void func2() { printf("in func2 function!/n"); longjmp(jmpbuffer, 1); }

 

编译:gcc main.c

运行:./a.out

输出:

in main function!
in func1 function!
in func2 function!
get the error from jump return!

 

我们在程序中可以看到,我们在main函数用setjmp中设置了一个接受跳转返回的点,在func2函数中用longjmp跳转返回。

在跳转返回后,我们直接退出函数,那么后面的

    printf("main function end!/n");
    return 0;
这两个语句将得不到执行。

 

前面我们说过longjmp函数中的val参数用来確定我们的jump是在哪产生的,下面我们修改一下前面的例子,看看怎样区别不同的跳转点。

#include <stdio.h> #include <setjmp.h> jmp_buf jmpbuffer; void func1(); void func2(); int main(void) { printf("in main function!/n"); int errNum = 0; errNum = setjmp(jmpbuffer); if(errNum != 0) { if (errNum == 1) printf("get the error from func1 function !/n"); if (errNum == 2) printf("get the error from func2 function !/n"); return -1; } func1(); printf("main function end!/n"); return 0; } void func1() { printf("in func1 function!/n"); longjmp(jmpbuffer, 1); func2(); } void func2() { printf("in func2 function!/n"); longjmp(jmpbuffer, 2); }

 

编译:gcc main.c

运行:./a.out

输出:

in main function!
in func1 function!
get the error from func1 function !

 

注意:不可这样

if(setjmp(jmpbuff) == 1)

  ........

if(setjmp(jmpbuff) == 2)

  ........

这样会在main中设置两个跳转返回点,而且是同一个jmpbuff,那么前面一个会被后面一个覆盖,则用func1中的跳转返回点就是后面的那个,在main函数中就会比较返回值是否为2,因为在func1中跳转返回是1,因此会再次执行后面的func1函数,那么就会陷入死循环中了。

 

我们知道,在一个进程中,当我们调用一个函数时,我们的进程就会在栈中分配一个新栈区给这个函数,当这个函数返回时,就销毁这个栈区。

因此,当我们进行函数间的跳转时,我们上层的函数的某些变量的值是得不到保存的。我们可以看下是哪些变量受到影响。

我们修改第一个例子:

#include <stdio.h> #include <setjmp.h> jmp_buf jmpbuffer; void func1(int, int, int, int); void func2(); int globVal; int main(void) { 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("get the error from jump return!/n"); printf("globVal = %d, autoVal = %d, regiVal = %d, volaVal = %d, statVal = %d/n", globVal, autoVal, regiVal, volaVal, statVal); return -1; } globVal = 101, autoVal = 102, regiVal = 103, volaVal = 104, statVal = 105; func1(autoVal, regiVal, volaVal, statVal); printf("main function end!/n"); return 0; } void func1(int i, int j, int k, int l) { printf("in func1 function!/n"); printf("globVal = %d, autoVal = %d, regiVal = %d, volaVal = %d, statVal = %d/n", globVal, i, j, k, l); func2(); } void func2() { printf("in func2 function!/n"); longjmp(jmpbuffer, 1); }

 

第一次,我们尝试不优化编译:

编译:gcc main.c

运行:./a.out

输出:

in func1 function!
globVal = 101, autoVal = 102, regiVal = 103, volaVal = 104, statVal = 105
in func2 function!
get the error from jump return!
globVal = 101, autoVal = 102, regiVal = 103, volaVal = 104, statVal = 105

 

第二次,我们进行优化编译:

编译:gcc -O main.c

运行:./a.out

输出:

in func1 function!
globVal = 101, autoVal = 102, regiVal = 103, volaVal = 104, statVal = 105
in func2 function!
get the error from jump return!
globVal = 101, autoVal = 2, regiVal = 3, volaVal = 104, statVal = 105

 

我们可以看到,全局、静态和易失性变量不受优化的影响,它们的值是最近变化的值。这也告诉我们如果我们写一个非局部跳转的程序,就应该使用volatile属性的变量。

你可能感兴趣的:(c,优化,function,gcc,存储,语言)