非本地跳转之setjmp与longjmp

非本地跳转(unlocal jump)是与本地跳转相对应的一个概念。

本地跳转主要指的是类似于goto语句的一系列应用,当设置了标志之后,可以跳到所在函数内部的标号上。然而,本地跳转不能将控制权转移到所在程序的任意地点,不能跨越函数,因此也就有了非本地跳转

C语言里面提供了setjmplongjmp函数来进行跨越函数之间的控制权的跳转,从而称之为非本地跳转。

#include <setjmp.h>
int setjmp(jmp_buf env);

该函数主要用来保存当前执行状态,作为后续跳转的目标。调用时,当前状态会被存放在env指向的结构中,env将被 long_jmp 操作作为参数,以返回调用点,跳转的结果看起来就好像刚从setjmp返回一样。

需要注意的是,第一次调用setjmp的时候返回值为0;而从long_jmp操作返回时,返回值是非0的,数值与longjmp传入的参数value有关。通过判断setjmp的返回值,就可以判断当前执行状态。

#include<setjmp.h>
void long_jmp(jmp_buf env, int value);

该函数用来恢复env中保存的执行状态,另一参数value用来传递返回值给跳转目标。如果value值为0,则跳转后返回setjmp处的值为1;否则,返回setjmp处的值为value

因此,整个非本地跳转的执行过程是:首先,在程序中调用setjmp进行当前运行栈环境的保存,接着在程序的其它地方调用longjmp进行跳转,跳转回的位置就是该setjmp的位置。

这其中需要注意的是:jmp_buf类型的变量env,因为该变量保存的是当前执行位置的运行栈环境;因此如果需要还能跳转到这个位置,那么longjmp必须能调用到这个env变量,因此这个变量一般为全局的。

接下来,简单的介绍一下运行栈的概念。

运行栈

简单的来说,程序运行的时候如果一个函数Fa内部调用了另一个函数Fb,那么当Fb执行完了之后,控制权如何返回给Fa,并继续执行Fa后面的语句呢?这就使用到了运行栈。

简要的说,运行栈里按照函数调用的顺序将一个个函数压入栈中,当一个函数执行结束之后,这个函数的栈帧(stack frame)就会从运行栈中pop出来,并将控制权(PC)转向调用函数(callee),控制权的记录就在每个函数所对应的的栈帧中。

因此,如果我们有程序段如下:

void Fa()
{
    ...
    Fb();
    ...
}
void Fb()
{
    ...
    Fc();
    ...
}
void Fc()
{
    ...
    //do something here
}

void main()
{
    ...
    Fa();
}

那么,当调用到函数Fc时程序的运行栈大致如下图所示:

当执行完Fc之后就会将Fc的栈帧pop出来,如下图所示:

非本地跳转模拟异常处理机制

当使用非本地跳转时,就不需要一层层的解开程序调用栈,而是直接将控制流转移到对应的位置。

在最初,还没有实现异常处理机制的时候,有的时候会用非本地跳转来模拟异常处理机制,大致思路如下:

#define myTry if(setjmp(env) == 0){

#define myCatch(err) }else if(err != NULL){                                    //TO DO SOMETHING HERE                                            \
                               }

#define myThrow(err) longjmp(env, err.ToInteger())

在myTry处设置setjmp,并保存当前的程序运行栈到env中,当程序中某个函数throw出一个异常时,就使用longjmp进行跳转。此时,程序又回到了setjmp处,但是因为返回值已经被设置为err.ToInteger(),因此setjmp的返回值肯定不是0,于是程序就进入到了else if分支,即myCatch语句块中。

但是,使用非本地跳转来模拟异常处理机制时会产生一定的问题,那就是在语句块中定义的局部变量所占的内存并不会被正常的释放,导致内存泄漏。

因为,非本地跳转在发生跳转时是直接将程序的控制流转移过去,而不是进行正常的栈退解(stack unwinding)操作,因此并不能识别出其中的变量并进行析构,不过现在的一些编译器已经实现了相应的功能,因编译器而异。

时间: 2025-02-01 04:44:02

非本地跳转之setjmp与longjmp的相关文章

【转】浅析C语言的非局部跳转:setjmp和longjmp

转自 http://www.cnblogs.com/lienhua34/archive/2012/04/22/2464859.html C语言中有一个goto语句,其可以结合标号实现函数内部的任意跳转(通常情况下,很多人都建议不要使用goto语句,因为采用goto语句后,代码维护工作量加大).另外,C语言标准中还提供一种非局部跳转“no-local goto",其通过标准库<setjmp.h>中的两个标准函数setjmp和longjmp来实现. C标准库<setjmp.h>

非本地跳转

首先介绍下与非本地跳转的对应的本地跳转,本地跳转指的是类似于goto语句的一系列应用,当设置了标志之后,可以跳到所在函数内部的标号上.然而,本地跳转不能将控制权转移到所在程序的任意地点,不能跨越函数,因此也就有了非本地跳转. 1.非本地跳转是C语言提供的一种用户级的异常控制流的形式,它将控制直接从一个函数转移到另一个当前正在执行的函数,而不需要经过正常的调用-返回序列. 非本地跳转是通过setjmp和longjmp函数提供的. 我们来看看函数: 1 #include <setjmp.h> 2

C语言中setjmp与longjmp学习笔记

一.基础介绍 ?? ?头文件:#include<setjmp.h> ?? ?原型:??int?setjmp(jmp_buf envbuf) ?? ?宏函数setjmp()在缓冲区envbuf中保存系统堆栈里的内容,供longjmp()以后使用.首次调用setjmp()宏时,返回值为0,然而longjmp()把一个变原传递给setjmp(),该值(恒不为0)就是调用longjmp()后出现的setjmp()的值. void longjmp(jmp_buf envbuf,int status);

【转载】setjmp和longjmp函数使用详解

[说明]本文上半部分转载自 wykwdy007 的转载文章 http://blog.csdn.net/wykwdy007/article/details/6535322 -------------------------------------------------- 非局部跳转语句---setjmp和longjmp函数.非局部指的是,这不是由普通C语言goto,语句在一个函数内实施的跳转,而是在栈上跳过若干调用帧,返回到当前函数调用路径上的某一个函数中.#include <setjmp.h>

linux setjmp与longjmp的使用

1.setjmp setjmp的工作原理: 调用这个函数的时候,它会保存执行现场,并返回0:之后调用longjmp,可恢复到setjmp保存的现场,setjmp再次返回,不过这次该函数返回非0 输出结果: 通过setjmp和longjmp捕获异常 参考链接: 宋宝华:让Linux的段错误(segmentation fault)不再是一个错误  https://mp.weixin.qq.com/s/cemhlZMqGOoLpzKe4Vm5TQ 原文地址:https://www.cnblogs.co

C中setjmp和longjmp用法

C中提供了goto语法,可以通过goto跳转到函数体内部标记的某一行代码,但是却无法跳出函数之外的任意位置. 为了解决这个限制,C函数库提供了setjmp()和longjmp()函数,它们分别承担非局部位置记录标号和goto作用. 使用这两个函数需要包含头文件<setjmp.h> int setjmp(jmp_buf env) 建立本地的jmp_buf缓冲区并且初始化,用于将来跳转回此处.这个子程序保存程序的调用环境于env参数所指的缓冲区,env将被longjmp使用.如果是从setjmp直

setjmp()、longjmp() Linux Exception Handling/Error Handling、no-local goto

目录 1. 应用场景 2. Use Case Code Analysis 3. 和setjmp.longjmp有关的glibc and eglibc 2.5, 2.7, 2.13 - Buffer Overflow Vulnerability 1. 应用场景 非局部跳转通常被用于实现将程序控制流转移到错误处理模块中:或者是通过这种非正常的函数返回机制,返回到之前调用的函数中 1. setjmp.longjmp的典型用途是异常处理机制的实现:利用longjmp恢复程序或线程的状态,甚至可以跳过栈中

setjmp和longjmp简介

setjmp和longjmp简介 1setjmp和longjmp简介 与刺激的abort函数和exit函数相比,goto语句看起来是处理异常的更可行方案.但是goto是本地的,它只能跳到所在函数内部的标号上,而不能将控制权转移到所在程序的任意地点(当然,除非你的所有代码都在main体中).为了解决这个限制,C函数库提供了setjmp函数和longjmp函数,它们分别承担非局部标号和goto作用.头文件<setjmp.h>申明了这些函数及同时所需的jmp_buf数据类型. 原理非常简单: 1.

setjmp()和longjmp()

1.语言没有异常处理机制,可以使用setjmp和longjmp进行模拟,另外,goto语句不能在函数间跳转,可以使用setjmp和longjmp完成函数间的跳转. 使用setjmp()函数需要包含头文件<setjmp.h> setjmp()函数用于建立本地的jmp_buf缓冲区并初始化,用于将来跳转到这个地方. longjmp()函数恢复jmp_buf变量所保存的信息,longjmp()之后返回setjmp()处执行. 1.1使用setjmp处理异常的例子: # include <std