当我们在调用函数时,函数的调用的嵌套的层次很深的话,如果出错,那么一层一层的返回和判断就很麻烦,所以,如果出错,就可以直接返回到最上面的调用的函数就会很方便。
注意:
goto是局部跳转,只能在一个函数内实施的跳转;
setjmp()和longjmp()是非局部跳转,结合一个jmp_buf类型的变量,可以在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。
其原型如下:
#include <setjmp.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
函数setjmp存储当前的堆栈环境(包括程序当前的执行位置)到参数env中,当函数正常调用成功时返回值为0。函数longjmp恢复保存在env中的堆栈信息,并 使程序转移到env中保存的位置处重新执行。两个函数联合来使用。
函数 longjmp 调用成功后,程序转移到函数setjmp处执行,函数setjmp返回val。如果参数val取值为0,为了与上次正常调用setjmp相区别,函数setjmp将自动返回1。
考虑这样的函数调用序列:
main --> do_line --> cmd_add
对应的栈的使用情况大致如下:
在编写这样的程序时经常会遇到的一个问题是,如何处理非致命性的错误。
例如,cmd_add函数发现一个错误,那么它可能希望忽略函数内的余下部分,返回main函数继续执行。但是如果这种情况出现在main函数中的深层嵌套中时,用C就比较难以做到这一点。如果我们不得不以检查返回值的方法逐层返回,那么就会变得很麻烦。
解决这种问题的方法就是使用setjmp()和longjmp()函数。
以上面说的为例,在main函数中希望返回到的位置上调用setjmp()。直接调用setjmp()时,它会将所有与当前处理器相关的状态信息(比如指令指针的内容、运行时栈指针等)保存到jmp_buf中去并返回0。
在cmd_add函数中可能出现错误并返回的位置上调用longjmp(),该函数返回时就好像刚从setjmp()中返回时一样--又回到了刚刚从setjmp()返回的地方。只不过这一次返回值是调用longjmp()时所使用的第二个参数int val,因此可以通过这个值断定程序是从longjmp()返回的。
setjmp()和longjmp()不能用于C++
这是因为longjmp()不能识别对象。特别是当跳出某个作用域时,它不会调用对象的析构函数(调用析构函数的唯一证据是包含该对象的右大括号)。而析构函数的调用在C++中是必需的操作。
实际上,C++标准中已经说明,使用goto跳入某个作用域(有效地跳过构造函数的调用),或使用longjmp()跳出某个作用域而且这个作用域的栈中有某个对象需要析构时,程序的行为是不确定的。