c语言中的局部跳转及全局跳转功能

 更新时间:2024年09月19日 10:47:20   作者:标标大人  
本文介绍了C语言中的goto语句,以及如何使用setjmp和longjmp实现跨函数的跳转,详细讲解了setjmp和longjmp的使用方法和注意事项,以及使用这种全局跳转后变量状态的不确定性,感兴趣的朋友一起看看吧

一、前言

在c语言中,当我们在处理某些异常情况的时候,经常会使用goto语句来进行跳转。goto用起来很方便,但可能很多人都不知道,goto只能在一个函数里面跳转,并不能够跨函数跳转。本文将介绍能够跨函数跳转的接口setjmp和longjmp,将围绕如下内容展开:
1.goto的局限性
2.进程运行时的栈帧结构
3.setjmp和longjmp简介和使用方法
4.使用了全局跳转后main中变量的状态

二、goto的局限性

goto只能在同一个函数里面跳转,无法从一个函数跳转到另一个函数中。如果试图从一个函数跳转到另一个函数,则编译会报错。参考代码如下:

/*************************************************************************
        > File Name: goto_test.c
        > Author: conbiao
        > Created Time: 2024年09月13日 星期五 10时47分41秒
 ************************************************************************/
/***********************************************************************
 *                             HEADER
 **********************************************************************/
#include<stdio.h>
/***********************************************************************
 *                              MACRO
 **********************************************************************/
/***********************************************************************
 *                          GLOBAL VARIABLE
 **********************************************************************/
/***********************************************************************
 *                       FUNCTION DESCRIPTION
 **********************************************************************/
void goto_test(void);
/***********************************************************************
* FUNCTION NAME: void goto_test()
 ***********************************************************************
*
* Summary:
*       This function is used to test goto.
*
* Params:
*       NONE.
*
* Return:
*       NONE.
*
***********************************************************************/
void goto_test(void)
{
    int i;
start:
    i = 0;
    printf("%s: start!\n",__func__);
    while(1)
    {
        printf("%s: i = %d\n",__func__,i++);
        if(i > 5)
            goto main_start;
    }
}
/***********************************************************************
 *                                MAIN
 **********************************************************************/
int main(int argc, char *argv[])
{
    int ret = 0;
main_start:
    printf("%s: start!\n",__func__);
    goto_test();
    return ret;
}

编译结果如下:

(2-1)
如果在同一个函数中使用goto,则能够正常跳转,代码如下:

/*************************************************************************
        > File Name: goto_test.c
        > Author: conbiao
        > Created Time: 2024年09月13日 星期五 10时47分41秒
 ************************************************************************/
/***********************************************************************
 *                             HEADER
 **********************************************************************/
#include<stdio.h>
/***********************************************************************
 *                              MACRO
 **********************************************************************/
/***********************************************************************
 *                          GLOBAL VARIABLE
 **********************************************************************/
/***********************************************************************
 *                       FUNCTION DESCRIPTION
 **********************************************************************/
void goto_test(void);
/***********************************************************************
* FUNCTION NAME: void goto_test()
 ***********************************************************************
*
* Summary:
*       This function is used to test goto.
*
* Params:
*       NONE.
*
* Return:
*       NONE.
*
***********************************************************************/
void goto_test(void)
{
    int i;
start:
    i = 0;
    printf("%s: start!\n",__func__);
    while(1)
    {
        printf("%s: i = %d\n",__func__,i++);
        if(i > 5)
            goto start;
    }
}
/***********************************************************************
 *                                MAIN
 **********************************************************************/
int main(int argc, char *argv[])
{
    int ret = 0;
main_start:
    printf("%s: start!\n",__func__);
    goto_test();
    return ret;
}

运行结果如下:

(2-2)

三、进程运行时的栈帧结构

在https://editor.csdn.net/md/?articleId=142054973一节中已经介绍过c程序运行时的存储空间布局,这其实是一个进程的进程空间结构。其中,栈空间用于存储程序在运行过程中使用到的临时变量,在进程空间中,系统为每个函数分配一个栈帧,结构如下:

goto只能在其调用函数的栈帧中跳转,无法从一个栈帧跳转到另一个栈帧。

四、setjmp和longjmp

使用setjmp和longjmp能够实现在栈帧上进行跳跃,其中setjmp用于设置标记,它会保存当前的执行环境(如程序计数器、栈指针等)在调用longjmp后,会恢复之前保存的执行环境,相当于跳转到setjmp的位置,。

4.1 setjmp

setjmp的函数实现如下:

头文件:#include <setjmp.h>
函数原型: int setjmp(jmp_buf env);
返回值
如果 setjmp 是直接调用的,它将返回 0。
如果 setjmp 是通过 longjmp 调用的,它将返回 longjmp 的第二个参数(非零值)。
传入参数
jmp_buf env:这是一个用于存储执行环境的数组类型。setjmp 会将当前的执行环境保存到这个数组中。

4.2 longjmp

longjmp的函数原型如下:

头文件: #include <setjmp.h> 函数原型: void longjmp(jmp_buf env, int val);
传入参数:
jmp_buf env:这是之前由 setjmp 保存的执行环境。
int val:这是 longjmp 返回给 setjmp 的值。通常是一个非零值,用于区分不同的跳转点。

4.3 参考代码

/*************************************************************************
        > File Name: jump_test.c
        > Author: conbiao
        > Created Time: 2024年09月13日 星期五 14时24分11秒
 ************************************************************************/
/***********************************************************************
 *                             HEADER
 **********************************************************************/
#include<stdio.h>
#include<setjmp.h>
/***********************************************************************
 *                              MACRO
 **********************************************************************/
/***********************************************************************
 *                          GLOBAL VARIABLE
 **********************************************************************/
jmp_buf env;
static int i = 0;
/***********************************************************************
 *                       FUNCTION DESCRIPTION
 **********************************************************************/
void func1(void);
/***********************************************************************
* FUNCTION NAME:
 ***********************************************************************
*
* Summary:
*
* Params:
*
* Return:
*
***********************************************************************/
void func1(void)
{
    printf("%s: start!\n",__func__);
    while(i < 8)
    {
        longjmp(env,i++);
    }
}
/***********************************************************************
 *                                MAIN
 **********************************************************************/
int main(int argc, char *argv[])
{
    int ret = 0;
    ret = setjmp(env);
    if(ret == 0)
    {
        printf("%s: This is the first call setjump!\n",__func__);
    }
    else
    {
        printf("%s: This is return from longjmp! ret = %d\n",__func__,ret);
    }
    func1();
    return ret;
}

运行结果如下:

(4.3-1)
可见,使用setjmp和longjmp实现了从func1的栈帧跳转到main函数所在的栈帧的功能。

分析一下上面代码的执行过程,在main函数调用setjmp前,该进程的栈如下:

(4.3-2)
到执行func1函数,调用longjmp前,栈空间如下:

(4.3-3)
在setjmp时,会将当前的执行环境信息保存到env中,当执行longjmp时,栈空间会回到4.3-2的情况,func1的栈帧就没有了。

五、使用全局跳转后main函数中变量的状态

上文已经说明了,使用longjmp会使程序回退到setjmp前的状态。那么,如果main函数中在调用setjmp前定义了一个变量,在调用setjmp后改变了这个变量的值,那么当调用longjmp后,该变量的值是调用setjmp前的还是后的呢?
c标准表示这个状态是不确定的。
如果希望调用longjmp后该变量的值不变,那么可以将该变量定义为全局变量或者使用static修饰,亦或者使用volatile属性声明。

参考资料:

《UNIX环境高级编程(第3版) (史蒂文斯 (W.Richard Stevens) 拉戈 (Stephen A.Rago))
(Z-Library)》

到此这篇关于c语言中的局部跳转以及全局跳转的文章就介绍到这了,更多相关c语言跳转内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • QT实现贪吃蛇游戏代码详解

    QT实现贪吃蛇游戏代码详解

    本文主要为大家详细介绍了在QT中实现贪吃蛇游戏的详细教程,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-11-11
  • C语言创建windows窗口实例

    C语言创建windows窗口实例

    这篇文章主要介绍了C语言创建windows窗口实例,本文直接给出实现代码,同时讲解了编码的步骤,需要的朋友可以参考下
    2015-04-04
  • Qt使用QWT绘制柱状图详解

    Qt使用QWT绘制柱状图详解

    QT中提供了一个叫做QWT的库。QWT,全称是Qt Widgets for Technical Applications,是一个基于LGPL版权协议的开源项目,可生成各种统计图。本文将通过它绘制柱状图,需要的可以参考一下
    2022-01-01
  • QT已有项目导入工程时注意事项图文详解

    QT已有项目导入工程时注意事项图文详解

    QT开发这几年大大小小项目做了不少,花了点时间对知识点总结整合了一部分,下面这篇文章主要给大家介绍了关于QT已有项目导入工程时注意事项的相关资料,需要的朋友可以参考下
    2023-11-11
  • C++ Vector 动态数组的实现

    C++ Vector 动态数组的实现

    这篇文章主要介绍了C++ Vector 动态数组的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-01-01
  • C++开发的Redis数据导入工具优化

    C++开发的Redis数据导入工具优化

    这篇文章主要介绍了C++开发的Redis数据导入工具优化方法的相关资料,需要的朋友可以参考下
    2015-07-07
  • 详解C语言之文件操作下)

    详解C语言之文件操作下)

    这篇文章主要介绍了关于C语言文件操作方法的相关资料,小编觉得这篇文章写的还不错,需要的朋友可以参考下,希望能够给你带来帮助
    2021-11-11
  • 关于C++中引用和指针的区别详解

    关于C++中引用和指针的区别详解

    这篇文章主要介绍了关于C++中引用和指针的区别详解,
    2023-07-07
  • Opencv3.4.0实现视频中的帧保存为图片功能

    Opencv3.4.0实现视频中的帧保存为图片功能

    这篇文章主要为大家详细介绍了Opencv3.4.0实现视频中的帧保存为图片功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-05-05
  • Qt 自定义分页控件的实现

    Qt 自定义分页控件的实现

    在应用程序开发时经常会遇到数据分页的需求,每一页展示特定数量的数据,通过点击按钮翻页或者输入页码跳转到指定页,本文就来介绍一下Qt 自定义分页控件的实现,感兴趣的可以了解一下
    2023-11-11

最新评论