当前位置:首页 > 技术文章 > 正文内容

C|函数的调用与返回,本地与非本地跳转

arlanguage1个月前 (04-01)技术文章18

函数是程序的基本构件。函数可由三种单一入口和出口的基本控制结构(顺序、选择、循环)组成,函数内部也可以通过goto实现局部跳转,函数之间能够通过栈机制实现函数调用和返回,类似的,通过适当的语法机制来保存上下文环境,函数之间也能实现非局部跳转。

1 函数调用与返回

主调函数调用被调函数,流程控制从主调函数跳转到被调函数之前,上下文环境保存在栈帧上,被调函数执行完后,返回到被调函数的调用点之后:

2 goto局部跳转

我们知道,汇编语言可以实现条件或无条件跳转,在高级语言中,goto语句也可以跳转。在结构体编程语言中,程序块由顺序、选择、循环三种结构取代。

3 非局部跳转(Non local jumps)

The tools provided through the header file setjmp.h allow the programmer to bypass the normal function call and return discipline, by providing the means to perform jumps preserving the calling environment.

通过此头文件setjmp.h提供的工具,程序员可以通过提供执行跳转的方法来保留调用环境,从而绕过正常的函数调用和返回规程。

The header provides, a function, a macro with functional form and a specific type:

头文件提供函数、宏以及函数形式和特定类型:

3.1 setjmp()

在特定的需要函数的返回点调用setjmp即可以保存该处的上下文环境(jmp_buf):

typedef struct __JUMP_BUFFER {
    unsigned long Ebp;
    unsigned long Ebx;
    unsigned long Edi;
    unsigned long Esi;
    unsigned long Esp;
    unsigned long Eip;
    unsigned long Registration;
    unsigned long TryLevel;
    unsigned long Cookie;
    unsigned long UnwindFunc;
    unsigned long UnwindData[6];
} _JUMP_BUFFER;

#define _JBLEN  16
#define _JBTYPE int
typedef _JBTYPE jmp_buf[_JBLEN];

int setjmp (jmp_buf env);

This macro with functional form fills env with information about the current state of the calling environment in that point of code execution, so that it can be restored by a later call to longjmp.

这个函数形式的宏向env填充了关于代码执行点中调用环境的当前状态的信息,以便稍后调用longjmp时可以恢复。

Calling longjmp with the information stored in env restores this same state and returns the control to that same point (the call to setjmp), which is evaluated as a particular non-zero value.

使用存储在env中的信息调用longjmp会恢复相同的状态,并将控制返回到相同的点(对setjmp的调用),该点被计算为特定的非零值。

The state of the calling environment includes the values of all accessible objects, except those of automatic duration local to the function which do not have volatile-qualified types and which change before the call to longjmp; these have indeterminate values.

调用环境的状态包括所有可访问对象的值,但函数本地的自动持续时间对象除外,这些对象没有易失性限定类型,并且在调用longjmp之前更改;这些值不确定。

The invocation of setjmp shall be an expression statement by itself, or be evaluated in a selection or iteration statement either as the (potentially negated) entire controlling expression or compared against an integer constant expression. Otherwise, it causes undefined behavior.

setjmp的调用本身应该是一个表达式语句,或者在选择或迭代语句中作为(可能被否定的)整个控制表达式进行计算,或者与整数常量表达式进行比较。否则,会导致未定义的行为。Restores the environment to the state indicated by env, evaluating the setjmp expression that filled env as val.

3.2 longjmp

将环境恢复到env指示的状态,并将填充env的setjmp表达式计算为val。

void longjmp (jmp_buf env, int val);

The called function containing the longjmp() macro never returns to the point where it has been invoked. Instead, the function transfers the control to the point where setjmp was last used to fill the env, and evaluates the whole expression as val (unless this is zero, in which case it evaluates as value of 1).

包含longjmp()宏的被调函数永远不会返回到调用它的位置。相反,该函数将控件传输到上次使用setjmp填充env的位置,并将整个表达式计算为val(除非这是零,在这种情况下,它计算为值1)。

If env was not filled by a previous call to setjmp or if the function with such call has terminated execution, it causes undefined behavior.

如果之前对setjmp的调用未填充env,或者具有此类调用的函数已终止执行,则会导致未定义的行为。

In C++, the implementation may perform stack unwinding that destroys objects with automatic duration. If this invokes any non-trivial destructors, it causes undefined behavior.

在C++中,实现可以执行堆栈展开,以自动持续时间销毁对象。如果这调用任何非平凡的析构函数,则会导致未定义的行为。

demo code:

// setjmp和longjmp
#include 
#include 

void jmpfunc(jmp_buf env_buf)
{
    // ……
    printf("%s\n","③ 返回到返回点(setjmp()填充了jmp_buff的代码后)。");
    longjmp(env_buf, 110);
}
int main()
{
    int val;
    jmp_buf env_buffer;
    
    printf("① 在longjmp的预定返回点调用setjmp()保存此处的上下文环境jmp_buf,并恢复上下文环境。\n");
    val = setjmp( env_buffer );
    // longjmp返回点
    if( val != 0 ) 
    {
        printf("④ 从 longjmp() 返回,更新上下文环境jmp_buf,并返回值 = %d\n", val);
        goto label;
    }
    printf("② 包含了jmp_buf参数和longjmp()语句的函数调用。\n");
    jmpfunc( env_buffer );
label:
    printf("⑤ 此位置非jmpfunc函数的返回地址。\n");
    getchar();
    return(0);
}
/*
① 在longjmp的预定返回点调用setjmp()保存此处的上下文环境jmp_buf,并恢复上下文环境。
② 包含了jmp_buf参数和longjmp()语句的函数调用。
③ 返回到返回点(setjmp()填充了jmp_buff的代码后)。
④ 从 longjmp() 返回,更新上下文环境jmp_buf,并返回值 = 110
⑤ 此位置非jmpfunc函数的返回地址。
*/

-End-

扫描二维码推送至手机访问。

版权声明:本文由AR编程网发布,如需转载请注明出处。

本文链接:http://www.arlanguage.com/post/3772.html

分享给朋友:

“C|函数的调用与返回,本地与非本地跳转” 的相关文章

一文说清nginx规则匹配(含案例分析)

概述: 在nginx中,匹配分很多情形,例如:区分大小写匹配、不区分大小写匹配、有强匹配开头、有模糊匹配某些字符、有匹配后重写动作、有匹配反向代理动作、有匹配后终止操作、还有匹配全局变量类型等等,在很多情形中,匹配动作也存在优先策略,来看看下面的案例。案例一、 server { .............

SeaTunnel 实践 | SeaTunnel 帮你快速玩转 Spark 数据处理

Databricks 开源的 Apache Spark 对于分布式数据处理来说是一个伟大的进步。我们在使用 Spark 时发现了很多可圈可点之处,我们在此与大家分享一下我们在简化 Spark 使用和编程以及加快 Spark 在生产环境落地上做的一些努力。01一个 Spark Streaming 读取...

Linux进程管理工具 Supervisor详解

Supervisor安装与配置(linux/unix进程管理工具) Supervisor(http://supervisord.org)是用Python开发的一个client/server服务,是Linux/Unix系统下的一个进程管理工具,不支持Windows系统。它可以很方便的监听、启动、停止、...

Nginx 最全操作——nginx常用命令(3)

上一篇文章我们设置了nginx的配置,这里简单介绍一下nginx的常用命令。几个常用的命令:nginx -s reload # 向主进程发送信号,重新加载配置文件,热重启 nginx -s reopen # 重启 Nginx nginx -s stop # 快速关闭 nginx -s qu...

windows下配置nginx支持多个版本PHP

在本地开发时,经常需要不同版本到PHP,一般是通过给nginx配置不同到端口来解决nginx.conf配置文件中server段设置不同端口到PHP解析 server { listen 80; server_name www1.local.com;...

一篇文章带你FFmpeg到流媒体服务器开发

安装ffmpeg:下载FFmpeg和libx264的包ffmpeg-2.4.1.tar.bz2 last_x264.tar.bz2libx264需要yasm,所以先安装yasmapt-get install yasm 然后安装libx264aptitude install libx264-dev 也...