setjmp和longjmp的"非本地跳转"

本文介绍C语言中实现非本地跳转的setjmp和longjmp函数,包括它们的使用方法及注意事项,并给出两个示例程序,展示如何利用这两个函数进行异常处理。

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

C语言中要实现"非本地跳转",C标准函数库提供了2个函数setjmp和longjmp来实现这个功能。头文件在<setjmp.h>。setjmp/longjmp的典型用途是例外处理机制的实现:利用longjmp恢复程序或线程的状态,甚至可以跳过栈中多层的函数调用。

函数声明:

SYNOPSIS
       #include <setjmp.h>

       int setjmp(jmp_buf env);

DESCRIPTION
       A call to setjmp() shall save the calling environment in its env argument for later use by longjmp().

作用:建立本地的 jmp_buf缓冲区并且初始化,用于将来跳转回此处。这个子程序 [1] 保存程序的调用环境于 env参数所指的缓冲区, env将被 longjmp使用。如果是从 setjmp直接调用返回, setjmp返回值为0。如果是从 longjmp恢复的程序调用环境返回, setjmp返回非零值。


SYNOPSIS
       #include <setjmp.h>

       void longjmp(jmp_buf env, int val);

DESCRIPTION
       The longjmp() function shall restore the environment saved by the most recent invocation of setjmp() in the same thread, with the
       corresponding jmp_buf argument. If there is no such invocation, or if the function containing the invocation of setjmp() has ter-
       minated  execution  in the interim, or if the invocation of setjmp() was within the scope of an identifier with variably modified
       type and execution has left that scope in the interim, the behavior is undefined.   It is unspecified whether longjmp()  restores
       the signal mask, leaves the signal mask unchanged, or restores it to its value at the time setjmp() was called.

作用:恢复 env所指的缓冲区中的程序调用环境上下文, env所指缓冲区的内容是由 setjmp子程序 [1]调用所保存。 value的值从 longjmp传递给 setjmplongjmp完成后,程序从对应的 setjmp调用处继续执行,如同 setjmp调用刚刚完成。如果 value传递给 longjmp零值, setjmp的返回值为1;否则, setjmp的返回值为 value


示例1:

#include <stdio.h>
#include <stdlib.h>
#include <setjmp.h>

static jmp_buf buf;

int
main(void)
{
    volatile int b;
    b = 3;

    if (setjmp(buf) != 0)    //第一次调用返回0
    {   
        printf("%d\n", b); 
        exit(0);
    }   

    b = 5;
    longjmp(buf, 1);     //val为1时返回setjmp的跳转

    return 0;
}

运行结果:

5


示例2:

#include <setjmp.h>
#include "apue.h"

static void f1(int, int, int);
static void f2(void);

static jmp_buf jmpbuffer;

int
main(void)
{
    int count;
    register int val;
    volatile int sum;

    count = 2; val = 3; sum = 4;
    if (setjmp(jmpbuffer) != 0)
    {   
        printf("after longjmp: count = %d, val = %d, sum = %d\n",
                count, val, sum);
        exit(0);
    }   

    count = 97; val = 98; sum = 99; 
    f1(count, val, sum);

    return 0;
}

static void f1(int i, int j, int k)
{
    printf("in f1(): count = %d, val = %d, sum = %d\n",
            i, j, k);
    f2();
}

static void f2(void)
{
    longjmp(jmpbuffer, 1);
}

如果不使用优化选项编译时

[root@localhost src]# gcc -Wall -I../include fig7_5.c error.c -o fig7_5
[root@localhost src]# ./fig7_5 
in f1(): count = 97, val = 98, sum = 99
after longjmp: count = 97, val = 98, sum = 99

如果使用了优化选项后

[root@localhost src]# gcc -Wall -O -I../include fig7_5.c error.c -o fig7_5
[root@localhost src]# ./fig7_5
in f1(): count = 97, val = 98, sum = 99
after longjmp: count = 2, val = 3, sum = 99
从以上可以看出,易失变量(sum )不受优化的影响,在longjmp之后的值,是它在调用f1时的值。在我们所使用的setjmp(3)手册页上说明存放在存储器中的变量将具有longjmp时的值,而在CPU和浮点寄存器中的变量则恢复为调用setjmp时的值。这确实就是在运行程序7-5时所观察到的值。不进行优化时,所有这三个变量都存放在存储器中(亦即对val的寄存器存储类被忽略)。而进行优化时,count和val都存放在寄存器中(即使count并末说明为register) ,volatile变量则仍存放在存储器中。通过这一例子要理解的是,如果要编写一个使用非局部跳转的可移植程序,则必须使用volatile属性。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值