C++实操 - 常用于错误处理的goto和jmp语句

C/C++语言中goto语句,在结构化编程中一般不推荐使用。

goto 语句允许把控制无条件转移到同一函数内的被标记的语句。

一般可以用来跳出多层循环,或者应用于错误或异常处理(在程序运行不正常时停止操作,直接跳转到错误处理)。

而jmp语句功能更强大些,使用setjmp和longjmp的组合是 "超强的Goto"。这个功能是C标准库的功能,需要包含头文件setjmp.h。

setjmp/longjmp最关键的用途是它起到了 "非本地goto跳跃 "的作用。Goto命令(在极少数情况下,你需要在for和while循环中使用goto)在同一个作用域中使用是最安全的。如果你用goto跳过作用域(或跳过了一个发生堆栈自动分配操作的区间),你很可能会破坏你的程序的堆栈。setjmp/longjmp通过在你想跳到的位置保存堆栈信息来避免这种情况。然后,当你跳转时,它会加载这个堆栈信息。如果没有这个功能,C程序员很可能不得不转向汇编编程来解决只有setjmp/longjmp才能解决的问题。

在Ubuntu下,使用gcc来操作两个例子看一下。

goto语句示例:


#include <stdio.h>

#include <stdbool.h>

int main()

{

  bool isError = false;

  goto label;

  puts("1");

label:

  puts("2");

  if(isError)

    goto error;

  return 0;

error:

  return 1;

}



输出:2

C语言是格式非常灵活的语言,是以语句为执行单位,一行内可以有多个语句。

所以label定义的地方(label名加冒号),同一行前后都可以加语句;但goto到此label的话就会从后面的语句开始执行。

jmp语句示例:

这个例子演示了使用longjmp在程序出错时,能快速返回到顶层函数。


#include <stdio.h>

#include <setjmp.h>

jmp_buf env;

double divide(double a,double b)

{

    const double delta = 0.00000000001;         //由于浮点数不精确,所以需要定义个很小的数

    if(!((-delta<b)&&(b<delta)))

    {

        return  a/b ;

    }

    else

    {

        longjmp(env,1);                    //直接跳转到23行,ret=setjmp(env)代码处,并返回异常值(1)

        return 0;

    }

}





int main( )

{

    int ret;

    ret=setjmp(env); //手动调用 setjmp(),将返回正常值(0),





    if(!ret)

    {

        printf("5/0=%lf\n",divide(5,0));

    }

    else  if(ret==1)     //异常操作

    {

        printf("ERR\n");

    }

    return 0;

}

$ gcc -o test test.c

$ time ./test

ERR

real    0m0.001s
user    0m0.000s
sys    0m0.001s
 

Linux系统里自带的time命令,/usr/bin/time用来触发程序执行,并测量此程序的执行时间。

上面两个例子都使用C语言编译,如果使用C++编译器结果一样,C++兼容C。

$ gcc -o test test.c

$ g++ -o test test.cpp

jmp功能可以用来进行Error handling和多任务的同步处理(这个不好操作,并且使用其他技术明显更有优势)。

错误处理

假设在一个嵌套在许多其他函数中的函数深处有一个错误,而错误处理只在顶层函数中有意义。

如果中间的所有函数都必须正常返回并评估返回值或全局错误变量,以确定进一步的处理没有意义甚至是不好的,这将是非常繁琐和尴尬的。

这就是setjmp/longjmp的意义所在。这些情况类似于其他语言(C++、Java)中的异常有意义的情况。

使用时要特别小心。然而,正如其他人所解释的,longjmp对于摆脱讨厌的错误情况是非常有用的,当你想让我快速回到起点时,而不是要为18层的函数编排错误信息。

然而,就像goto,但更糟糕的是,你必须非常小心地使用它。一个longjmp只会让你回到代码的开头。它不会影响在setjmp和回到setjmp开始的地方之间可能已经发生变化的所有其他状态。因此,当你回到调用setjmp的地方时,分配的内存、锁定的信号、半初始化的数据结构等等,仍然是分配、锁定和半初始化的。这意味着,你必须小心使用的地方,在不引起更多问题的情况下,调用longjmp是可以的。当然,如果你做的下一件事是 "重启"[也许是在存储了一条关于错误的信息之后]--例如在一个嵌入式系统中,你发现硬件处于坏的状态,那当然没问题。

主要的区别在于,try/catch知道堆栈上的对象,并且知道如何为堆栈上分配的对象调用析构函数,而setjmp对此毫无作用。

另外,操作接口更加丰富,你可以定义不同种类异常类型,并在此基础上采取不同的行为。

The main difference is that try/catch is aware of the objects on the stack and know how to call dtors for objects allocated on the stack, which setjmp does nothing with this.

Also, the user interface is much richer, you can define several exception types and behaves differently based on that。

构造函数:constructor,ctor。

析构函数:destructor,dtor。

C++中使用try/catch的异常机制,如果程序正常运行,对性能影响不大,当出现异常需要处理时,对比C语言的jmp机制就会消耗更多的时间,但这毕竟是偶发的。

参考:

Practical usage of setjmp and longjmp in C - Stack Overflowicon-default.png?t=LA23https://stackoverflow.com/questions/14685406/practical-usage-of-setjmp-and-longjmp-in-c

C语言异常处理之 setjmp()和longjmp() - 诺谦 - 博客园icon-default.png?t=LA23https://www.cnblogs.com/lifexy/p/8820562.html

https://en.wikipedia.org/wiki/Setjmp.hicon-default.png?t=LA23https://en.wikipedia.org/wiki/Setjmp.h

The Cost of C++ Exceptions and setjmp/longjmp - Stack Overflowicon-default.png?t=LA23https://stackoverflow.com/questions/31486907/the-cost-of-c-exceptions-and-setjmp-longjmp

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

夜流冰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值