setjmp和longjmp函数使用详解


Posted on November 27, 2013


看到这两个函数,不认识,没用过,赶紧MARK,参考:

http://blog.csdn.net/chenyiming_1990/article/details/8683413

http://blog.codingnow.com/2010/05/setjmp.html

setjmp和longjmp函数是非局部跳转语句。非局部指的是,这和C语言常见的goto不一样,goto语句在一个函数内实施的跳转,而setjmp和longjmp是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中。使用这两上函数比goto还要容易出错,<u>所以不会用在安全软件上,而且也不是C89的标准。</u>

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

//调用此函数则返回到语句setjmp所在的地方,其中env 就是setjmp中的 env,而val 则是使setjmp的返回值变为val。  
Void longjmp(jmp_buf env,int val); 

当检查到一个错误时,调用longjmp函数(两个参数),第一个参数就是在调用setjmp时所用的env,第二个参数是具有非0值的val,它将成为从setjmp处返回的值。<strong>使用第二个参数的原因是对于一个setjmp可以有多个longjmp。</strong>

下面这个例子简单地讲述了这两个函数是如何使用的:

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

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

这两个函数可以用作异常处理,下面一个例子较为复杂,但可以更好地理解这两个函数。

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

标签:C语言

其它推荐日志:

Dreamweaver网页制作技巧

AJAX进度条的研究

【FTP】批处理上传文件至FTP服务器上某个目录下

重温字符编码ASCII,Unicode和UTF-8

买神舟笔记本需要注意的几点

找回“显示桌面”快捷方式

关于br标签高度的问题

无法定位程序输入点LdrSetSessionName于动态链接库mfc40u.dll上

PHP分页显示

SyntaxHighlighter, a fully functional self-contained code syntax highlighter!

PHP字符串截取函数(解决中文乱码问题!支持UTF-8和GB2312)

在javascript中设置body的onload事件

什么是僵尸文件?

学习ajax-xmlhttp:open方法

Windows Vista资源管理器复制、移动、删除文件慢的7种不同的解决办法和几个现象

无需插件,轻松实现FIREFOX后台打开标签

Mime类型列表

如何在asp.net中用c#在获得当前系统登陆的帐户的Identity

IE和Firefox的js兼容性整理

关于linkbutton点击后弹出新窗口的代码

Activex 部件不能创建对象故障解决

为GridView-Delete列添加确认对话框(Easy)

c#中结构数组定义

关于ASP.NET中用Response.Write()方法响应导致页面字体变大的问题

无法将类型“ASP.Image_aspx”转换为“System.Web.UI.WebControls.Image”

修理Vista:音乐文件夹变黄了怎么办?

IE8不兼容你的网页 怎么办? - 简单开启兼容模式

ASP.NET页面状态管理——ViewState的使用

Sun:Java+Vista没问题

复制表格:Table2Clipboard 0.0.2.4

【FTP】批处理下载FTP服务器上某个目录下的文件

php啊,addslashes和stripslashes,烦

LPCTSTR是什么意思?-_-!

无意中发现了另外一种开发AJAX的方法,ajax.dll

c#链接数据库的问题

VS2005文档生成器

关于lvalue required as left operand of assignment

如何理解LPCTSTR

什么是系统分析师?

Substring的一个误区!


Search
最新评论
推荐日志
微信扫一扫