setjmp和longjmp函数使用详解

原创 2013年03月17日 13:34:45

在网上看到的,觉得很有用,copy过来的。

非局部跳转语句---setjmp和longjmp函数。非局部指的是,这不是由普通C语言goto,语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。
#include <setjmp.h>
Int setjmp(jmp_buf  env);
       返回值:若直接调用则返回0,若从longjmp调用返回则返回非0值的longjmp中的val值
Void longjmp(jmp_buf env,int val);
        调用此函数则返回到语句setjmp所在的地方,其中env 就是setjmp中的 env,而val 则是使setjmp的返回值变为val。
        当检查到一个错误时,则以两个参数调用longjmp函数,第一个就是在调用setjmp时所用的env,第二个参数是具有非0值的val,它将成为从setjmp处返回的值。使用第二个参数的原因是对于一个setjmp可以有多个longjmp。

#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结合使用时,它们必须有严格的先后执行顺序,也即先调用setjmp函数,之后再调用longjmp函数,以恢复到先前被保存的“程序执行点”。否则,如果在setjmp调用之前,执行longjmp函数,将导致程序的执行流变的不可预测,很容易导致程序崩溃而退出

2.  longjmp必须在setjmp调用之后,而且longjmp必须在setjmp的作用域之内。具体来说,在一个函数中使用setjmp来初始化一个全局标号,然后只要该函数未曾返回,那么在其它任何地方都可以通过longjmp调用来跳转到 setjmp的下一条语句执行。实际上setjmp函数将发生调用处的局部环境保存在了一个jmp_buf的结构当中,只要主调函数中对应的内存未曾释放 (函数返回时局部内存就失效了),那么在调用longjmp的时候就可以根据已保存的jmp_buf参数恢复到setjmp的地方执行。

-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

异常处理

在下例中,setjmp被用于包住一个例外处理,类似trylongjmp调用类似于throw语句,允许一个异常返回给setjmp一个异常值。下属代码示例遵从1999 ISO C standardSingle UNIX Specification:仅在特定范围内引用setjmp

  • ifswitch或它们的嵌套使用的条件表达式
  • 上述情况下与!一起使用或者与整数常值比较
  • 作为单独的语句(不使用其返回值)

遵从上述规则使得创建程序环境缓冲区更为容易。更一般的使用setjmp可能引起未定义行为,如破坏局部变量;编译器被要求保护或警告这些用法。但轻微的复杂用法如switch ((exception_type = setjmp(env))) { }在文献与实践中是常见的,并保持了相当的可移植性。

#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 */
}


 

setjmp, longjmp用法简介

setjmp和longjmp其实就是goto的升级版,goto只能在同一个函数中跳转,但是setjmp和longjmp可以实现在不同的函数之间跳转。 下面是一个简单的示例演示,代码+运行结果,几行注...
  • wzzfeitian
  • wzzfeitian
  • 2013年07月14日 16:51
  • 2611

C语言中利用setjmp和longjmp做异常处理

错误处理是任何语言都需要解决的问题,只有不能保证100%的正确运行,就需要有处理错误的机制。异常处理就是其中的一种错误处理方式。1 过程活动记录(Active Record)C语言中每当有一个函数调用...
  • smstong
  • smstong
  • 2016年02月24日 14:55
  • 2589

setjmp和longjmp函数使用详解

非局部跳转语句---setjmp和longjmp函数。非局部指的是,这不是由普通C语言goto,语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。#incl...
  • wykwdy007
  • wykwdy007
  • 2011年06月09日 23:40
  • 15988

setjmp与longjmp原理分析

一、setjmp与longjmp的用法参考下面的文章         http://blog.csdn.net/chenyiming_1990/article/details/8683413 二、s...
  • stillvxx
  • stillvxx
  • 2014年01月08日 11:43
  • 1479

setjmp/longjmp非局部跳转函数分析

之前就一直好奇setjmp()/longjmp()函数是怎么实现非局部跳转的,心中猜测应该是通过保存调用setjmp()函数处的栈上下文(stack context),之后通过函数longjmp()来...
  • origin_lee
  • origin_lee
  • 2015年04月15日 12:13
  • 760

C语言异常处理机制--setjmp的返回值

使用setjmp时必须使用头文件setjmp.h。#include "setjmp.h"jmp_buf jmpbuffer;   int setjmp(jmp_buf jmpbuffer); void...
  • wesleyluo
  • wesleyluo
  • 2010年03月03日 10:44
  • 1926

C中setjmp和longjmp用法

C中提供了goto语法,可以通过goto跳转到函数体内部标记的某一行代码,但是却无法跳出函数之外的任意位置。 为了解决这个限制,C函数库提供了setjmp()和longjmp()函数,它们分别承担非局...
  • oktears
  • oktears
  • 2015年01月28日 16:32
  • 1663

c语言的jmp_buf函数

#include #include #include static jmp_buf buf; main() { volatile int b; b =3; if(setjm...
  • wangkeyen
  • wangkeyen
  • 2016年02月13日 14:50
  • 1258

C++异常系列(一)----setjmp 与 longjmp

转载自 http://blog.csdn.net/chenyiming_1990/article/details/8683413setjmp与longjmp是非局部性跳转语句:非局部指的是,这不是由普...
  • xiaorenwuzyh
  • xiaorenwuzyh
  • 2015年07月07日 09:13
  • 258

setjmp和longjmp很难和C++相处

setjmp和longjmp很难和C++相处     C程序员唯有以setjmp和longjmp才能近似这样的行为。但是longjmp在C++中有一个严重的缺陷:当它调整栈的时候,无法调用局部对...
  • chenglinhust
  • chenglinhust
  • 2013年08月15日 08:57
  • 960
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:setjmp和longjmp函数使用详解
举报原因:
原因补充:

(最多只允许输入30个字)