linux——setjmp()和longjmp()函数的使用

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u014470361/article/details/83541340

setjmp()和longjmp()函数

      ~~~~~ 与刺激的abort()和exit()相比,goto语句看起来比较适合处理异常的情况。不过,goto是本地的,只能跳到所在函数内部的标号上,而不能将控制权转移到所在程序的任意地点。
      ~~~~~ 为了解决这个限制,C函数库提供了setjmp()和longjmp()函数。setjmp()进行非局部标号的设置,而longjmp()是实现跳转的功能,调转到设置的标号处setjmp()。
      ~~~~~ 头文件**<setjmp.h>**申明了这些函数及同时所需的jmp_buf数据类型。
      ~~~~~ 原理非常简单:
1.setjmp(j)设置“jump”点,用正确的程序上下文填充jmp_buf对象j。这个上下文包括程序存放位置、栈和框架指针,其它重要的寄存器和内存数据。当初始化完jump的上下文,setjmp()返回0值。也就是第一次调用setjmp(j),初始化成功了,返回0。
2. 以后调用longjmp(j,r)的效果就是一个非局部的goto或“长跳转”到由j描述的上下文处(也就是到那原来设置j的setjmp()处)。在作为长跳转的目标而被调用时(不是第一次初始化的时候),若longjmp(j,r)函数中r是大于0的整数,则跳转到setjmp(j)会返回r;若longjmp(j,r)中r是0,即longjmp(j,0),那么跳转到setjmp(j)后,setjmp(j)返回时1,不会返回0。记住,setjmp()是不会在longjmp(j,0)时返回0的。
      ~~~~~ 通过有两类返回值,setjmp()让你知道它正在被怎么使用。当第一次设置j时,setjmp(j)如你期望地执行;但当作为长跳转的目标时,setjmp()就从外面“唤醒”它的上下文。

实际代码测试

(1)测试1

#include <setjmp.h>
#include <stdio.h>
jmp_buf j;
void test1(void)
{
        longjmp(j, 1); /* jump to setjmp then do case 1 */
        printf("this line should never appear\n");
}


int main(void)
{

        switch (setjmp(j))
        {
        case 0:
                printf("setjmp(j)==0 mains: it is initialized\n");
                test1();
                printf("this line should never appear\n");
                break;
        case 1:
                printf("Case 1\n");
                break;
        default:
                break;
        }
        return 0;
}

gcc jmp1.c 编译, ./a.out运行后,结果如下:

setjmp(j)==0 mains: it is initialized
Case 1

      ~~~~~ main函数中,第一次执行setjmp(j),setjmp(j)返回0,进入case 0,打印setjmp(j)==0 mains: it is initialized信息;
      ~~~~~ 之后运行test1()函数,在test()函数中运行 longjmp(j, 1);直接跳转到main中的setjmp(j),main中和test1()中的printf(“this line should never appear\n”);语句是永远也不会执行的。
      ~~~~~ 跳转到main中的setjmp(j)后,setjmp(j)执行返回1,进行case 1 分支,打印Case 1,break跳出,程序结束。
(2)测试2

#include <setjmp.h>
#include <stdio.h>
jmp_buf j;
void test1(void)
{
        longjmp(j, 0);
        printf("this line should never appear\n");
}

int main(void)
{

        switch (setjmp(j))
        {
        case 0:
                printf("setjmp(j)==0 mains: it is initialized\n");
                test1();
                printf("this line should never appear\n");
                break;
        case 1:
                printf("Case 1\n");
                break;
        case 2:
                printf("Case 2\n");
                break;
        default:
                break;
        }
        return 0;
}

将test1()中longjmp(j, 1);更改为longjmp(j, 0),编译运行,结果如下:

setjmp(j)==0 mains: it is initialized
Case 1

第一次执行setjmp(j),进行初始化,返回0;
之后执行longjmp(j, 0);跳转到setjmp(j)返回1,不会返回0。
即上面强调过的:
在作为长跳转的目标而被调用时(不是第一次初始化的时候),若longjmp(j,r)函数中r是大于0的整数,则跳转到setjmp(j)会返回r;若longjmp(j,r)中r是0,即longjmp(j,0),那么跳转到setjmp(j)后,setjmp(j)返回时1,不会返回0。setjmp()是不会在longjmp(j,0)时返回0的。
(3)

#include <setjmp.h>
#include <stdio.h>
#include <signal.h>
jmp_buf j;
void signal_hander(int signer)
{
        longjmp(j, 3); /*get intterrupt sign , jump to setjmp ,do case 3, jump out */
}
void test1(void)
{
        longjmp(j, 2); /* jump to setjmp then do case 2 */
        printf("this line should never appear\n");
}

void test2(void)
{
        longjmp(j, 1); /* jump to setjmp then do case 1 */
        printf("this line should never appear\n");
}

int main(void)
{
        signal(SIGINT, &signal_hander);
        switch (setjmp(j))
        {
        case 0:
                printf("setjmp(j)==0 mains: it is initialized\n");
                test1();
                printf("this line should never appear\n");
                break;
        case 1:
                printf("jump to Case 2\n");
                test1();
                break;
        case 2:
                printf("jump to Case 1\n");
                test2();
                break;
        case 3:
                printf("interrupted then jump out\n");
                break;
        default:
                break;
        }
        return 0;
}

编译,运行,结果如下:
在这里插入图片描述
如上,main函数中第一次执行setjmp(j)进行初始化后,进入case 0 分支,执行test1();
test1()中执行longjmp(j,2),调试到转case 2分支,执行test2();
test2()中执行longjmp(j,1),调试到转case 1分支,执行test1();
所以程序正常运行时在case 1 和case 2 分支之间无限循环。
main函数中设置signal(SIGINT, &signal_hander);可以捕获中断信号;当按下ctrl +C ,捕获到中断信号,执行 signal_hander()函数,调用longjmp(j,3)后跳转到case 3 ,打印interrupted then jump out后程序break跳出结束。

总结:

      ~~~~~ setjmp(j)设置“jump”点,用正确的程序上下文填充jmp_buf对象j。这个上下文包括程序存放位置、栈和框架指针,其它重要的寄存器和内存数据。当初始化完jump的上下文,setjmp()返回0值。也就是第一次调用setjmp(j),初始化成功了,返回0。
      ~~~~~ 以后调用longjmp(j,r)的效果就是一个非局部的goto或“长跳转”到由j描述的上下文处(也就是到那原来设置j的setjmp()处)。在作为长跳转的目标而被调用时(不是第一次初始化的时候),若longjmp(j,r)函数中r是大于0的整数,则跳转到setjmp(j)会返回r;若longjmp(j,r)中r是0,即longjmp(j,0),那么跳转到setjmp(j)后,setjmp(j)返回时1,不会返回0。setjmp()是不会在longjmp(j,0)时返回0的。

猜你喜欢

转载自blog.csdn.net/u014470361/article/details/83541340
今日推荐