Posted on November 27, 2013
看到这两个函数,不认识,没用过,赶紧MARK,参考:
http://blog.csdn.net/chenyiming_1990/article/details/8683413
http://blog.codingnow.com/2010/05/setjmp.html
setjmp和longjmp函数是非局部跳转语句。非局部指的是,这和C语言常见的goto不一样,goto语句在一个函数内实施的跳转,而setjmp和longjmp是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。使用这两上函数比goto还要容易出错,<u>所以不会用在安全软件上,而且也不是C89的标准。</u>
#include <setjmp.h> // 返回值:若直接调用则返回0,若从longjmp调用返回则返回非0值的longjmp中的val值 Int setjmp(jmp_buf env); //调用此函数则返回到语句setjmp所在的地方,其中env 就是setjmp中的 env,而val 则是使setjmp的返回值变为val。 Void longjmp(jmp_buf env,int val);
当检查到一个错误时,调用longjmp函数(两个参数),第一个参数就是在调用setjmp时所用的env,第二个参数是具有非0值的val,它将成为从setjmp处返回的值。<strong>使用第二个参数的原因是对于一个setjmp可以有多个longjmp。</strong>
下面这个例子简单地讲述了这两个函数是如何使用的:
#include <stdio.h> #include <setjmp.h> static jmp_buf buf; void second(void) { printf("second\n"); // 打印 longjmp(buf,1); // 跳回setjmp的调用处 - 使得setjmp返回值为1 } void first(void) { second(); printf("first\n"); // 不可能执行到此行 } int main() { if ( ! setjmp(buf) ) { first(); // 进入此行前,setjmp返回0 } else { // 当longjmp跳转回,setjmp返回1,因此进入此行 printf("main\n"); // 打印 } return 0; } //程序将输出: //second //main
注意到虽然first()子程序被调用,"first"不可能被打印。"main"被打印,因为条件语句if(!setjmp(buf))被执行第二次。
使用setjmp和longjmp要注意以下几点:
1、setjmp与longjmp结合使用时,它们必须有严格的先后执行顺序,<b>也即先调用setjmp函数,之后再调用longjmp函数</b>,以恢复到先前被保存的“程序执行点”。否则,如果在setjmp调用之前,执行longjmp函数,将导致程序的执行流变的不可预测,很容易导致程序崩溃而退出;
2、<b>longjmp必须在setjmp调用之后</b>,而且longjmp必须在setjmp的作用域之内。具体来说,在一个函数中使用setjmp来初始化一个全局标号,然后只要该函数未曾返回,那么在其它任何地方都可以通过longjmp调用来跳转到 setjmp的下一条语句执行。实际上setjmp函数将发生调用处的局部环境保存在了一个jmp_buf的结构当中,只要主调函数中对应的内存未曾释放 (函数返回时局部内存就失效了),那么在调用longjmp的时候就可以根据已保存的jmp_buf参数恢复到setjmp的地方执行。
这两个函数可以用作异常处理,下面一个例子较为复杂,但可以更好地理解这两个函数。
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <setjmp.h> void first(void); void second(void); /* This program's output is: calling first calling second entering second second failed with type 3 exception; remapping to type 1. first failed, exception type 1 */ /* Use a file scoped static variable for the exception stack so we can access * it anywhere within this translation unit. */ static jmp_buf exception_env; static int exception_type; int main() { void *volatile mem_buffer; mem_buffer = NULL; if (setjmp(exception_env)) { /* if we get here there was an exception */ printf("first failed, exception type %d\n", exception_type); } else { /* Run code that may signal failure via longjmp. */ printf("calling first\n"); first(); mem_buffer = malloc(300); /* allocate a resource */ printf(strcpy((char*) mem_buffer, "first succeeded!")); /* ... this will not happen */ } if (mem_buffer) free((void*) mem_buffer); /* carefully deallocate resource */ return 0; } void first(void) { jmp_buf my_env; printf("calling second\n"); memcpy(my_env, exception_env, sizeof(jmp_buf)); switch (setjmp(exception_env)) { case 3: /* if we get here there was an exception. */ printf("second failed with type 3 exception; remapping to type 1.\n"); exception_type = 1; default: /* fall through */ memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */ longjmp(exception_env, exception_type); /* continue handling the exception */ case 0: /* normal, desired operation */ second(); printf("second succeeded\n"); /* not reached */ } memcpy(exception_env, my_env, sizeof(jmp_buf)); /* restore exception stack */ } void second(void) { printf("entering second\n" ); /* reached */ exception_type = 3; longjmp(exception_env, exception_type); /* declare that the program has failed */ printf("leaving second\n"); /* not reached */ }