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

原创 2015年07月07日 09:13:41

转载自 http://blog.csdn.net/chenyiming_1990/article/details/8683413

setjmp与longjmp是非局部性跳转语句:非局部指的是,这不是由普通C语言goto,语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。

#include<setjmp.h>
int setjmp(jmp_buf env);
    返回值:若直接调用则返回0,若从longjmp调用则返回非零值的 longjmp中的 val值。

void longjmp(jmp_buf env, int val);
    调用 longjmp 返回到语句 setjmp所在的地方,其中env就是 setjmp所设置的 env, val是使得 setjmp 的返回值变为 val.
    当检测到一个错误的时候,就以两个参数调用 longjmp,第一个参数是setjmp所设置的 env,第二个参水是非零值的val,它将成为 setjmp的返回值。 使用第二个参数的原因是对于一个setjmp 可以有多个 longjmp

代码如下:
setjmp的基本使用

上述程序输出: main
second。
注意,first 不可能被打印。


使用setjmp和longjmp要注意以下几点:

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

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


C 语言中的异常处理:

在下例中: (基本思想)
1. setjmp 用来包裹住一个可能出现异常的模块,将setjmp 放在 if 语句中。
2. 如果这个模块出现异常,那么调用 longjmp 直接返回到 setjmp的位置即可。
3. longjmp 类似于 throw 语句,允许一个异常返回给 setjmp 一个异常值。

下属代码示例遵从1999 ISO C standard与Single UNIX Specification:仅在特定范围内引用setjmp。

  • if, switch 语句 或他们嵌套使用的条件表达式
  • 上述情况 与 ! 一起使用或者与整数常数值 进行比较
  • 作为单独的语句

遵循上述规则使得创建 程序环境缓冲区变得更加容易。更一般的使用 setjmp 可能会引起未定义的行为,如破坏局部变量。 轻微的复杂用法 如 switch((exception = setjmp(env))) { } 在实践与文献中是常见的,并且具有相当的可移植性。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>  
#include <stdlib.h>  
#include <string.h>  
#include <setjmp.h>  
#include <string.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很难和C++相处

setjmp和longjmp很难和C++相处     C程序员唯有以setjmp和longjmp才能近似这样的行为。但是longjmp在C++中有一个严重的缺陷:当它调整栈的时候,无法调用局部对...
  • chenglinhust
  • chenglinhust
  • 2013年08月15日 08:57
  • 971

setjmp和longjmp函数使用详解

非局部跳转语句---setjmp和longjmp函数。非局部指的是,这不是由普通C语言goto,语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。 #inc...
  • chenyiming_1990
  • chenyiming_1990
  • 2013年03月17日 13:34
  • 14218

setjmp和longjmp函数使用详解

非局部跳转语句---setjmp和longjmp函数。非局部指的是,这不是由普通C语言goto,语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。 #inc...
  • chenyiming_1990
  • chenyiming_1990
  • 2013年03月17日 13:34
  • 14218

构建C协程之setjmp/long_jmp篇

原理简介 在标准C中的头文件中定义了一组函数 setjmp / long_jmp 用来实现“非本地跳转”的功能,利用 setjmp 可以保存当前执行线索状态,稍后通过 long_jmp 函数...
  • kobejayandy
  • kobejayandy
  • 2014年12月06日 22:16
  • 6124

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

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

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

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

SIGSEGV 信号捕捉,setjmp/longjmp记录上下文跳转

在linux中编程的时候 有时候 try catch 可能满足不了我们的需求。因为碰到类似数组越界 ,非法内存访问之类的 ,这样的错误无法捕获。下面我们介绍一种使用捕获信号实现的异常 用来保证诸如段错...
  • yangyangye
  • yangyangye
  • 2015年01月21日 08:39
  • 1399

setjmp和longjmp很难和C++相处

setjmp和longjmp很难和C++相处     C程序员唯有以setjmp和longjmp才能近似这样的行为。但是longjmp在C++中有一个严重的缺陷:当它调整栈的时候,无法调用局部对...
  • chenglinhust
  • chenglinhust
  • 2013年08月15日 08:57
  • 971

C异常处理机制:setjmp和longjmp

setjmp()和longjum()是通过操纵过程活动记录实现的。它是C语言所独有的。它们部分你不了C语言有限的转移能力。这个两个函数协同工作,如下所示:     *setjmp(jmp_buf j)...
  • bytxl
  • bytxl
  • 2015年01月16日 16:18
  • 1058

setjmp和longjmp函数使用方法

非局部跳转语句—setjmp和longjmp函数。非局部指的是,这不是由普通C语言goto,语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。#includ...
  • luomoshusheng
  • luomoshusheng
  • 2015年08月17日 21:33
  • 444
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++异常系列(一)----setjmp 与 longjmp
举报原因:
原因补充:

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