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

本文详细介绍了C语言中的setjmp()和longjmp()函数,这两个函数用于实现非局部跳转,提供了一种超越常规goto语句的能力,能够在程序的任意位置进行跳转。文章通过实例代码测试,展示了setjmp()的初始化过程以及longjmp()的跳转逻辑,特别强调了longjmp()函数中参数r的值对返回值的影响,并在最后总结了这两个函数的工作原理和使用要点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

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的。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值