C--异常处理

程序消亡的一般形式:
(1)无疾而终(即就是正常退出,例如return 0、点击图标按钮直接退出)
(2)自杀(例如:abort()、exit(0))

abort()函数:引发不正常进程的终止,异常终止一个进程,终止当前进程,返回错误码,错误码缺省值是3
函数原型 : void abort(void);
函数说明:abort函数是一个比较严重的函数,当调用它时,会导致程序异常终止,而不会进行一些常规的清除工作,比如释
放内存等。
该函数产生sigabrt信号并发送给自己,默认情况下,导致程序将终止不成功的终止错误代码返回到主机环境.

(3)他杀(任务管理器中关闭该程序)

C语言异常处理的一般方式:
(1).终止程序(除数为0)


#include<windows.h>
#include<iostream>
using namespace std;


void FunTest()
{
    int tmp = 0;
    int ret = 5/tmp;     //0不可以做除数,无意义
}

int main()
{
    FunTest();
    int ret = GetLastError();

    system("pause");
    return 0;//程序会直接崩溃
}

(2).返回一个表示错误的值,附加错误码(GetLastError())

int main()
{
    FILE *fp = fopen("1.txt","rb");

    cout<<errno<<endl;//errno 是记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定义
                      //errno底层是一个宏,表示错误码,此时输出结果为2
                      //注意:只有当一个库函数失败时,errno才会被设置。
                      //当函数成功运行时,errno的值不会被修改。
                      //这意味着我们不能通过测试errno的值来判断是否有错误存在。
                      //反之,只有当被调用的函数提示有错误发生时检查errno的值才有意义。

    int ret = GetLastError();//该函数返回调用线程最近的错误代码值,错误代码以单线程为基础来维护的,多线程不重写各自的错误代码值。
    //GetLastError返回的值通过在api函数中调用SetLastError或SetLastErrorEx设置。
    //函数并无必要设置上一次错误信息,所以即使一次GetLastError调用返回的是零值,也不能担保函数已成功执行。
    //只有在函数调用返回一个错误结果时,这个函数指出的错误结果才是有效的。
    //通常,只有在函数返回一个错误结果,而且已知函数会设置GetLastError变量的前提下,才应访问GetLastError;这时能保证获得有效的结果。
    //SetLastError函数主要在对api函数进行模拟的dll函数中使用,所以对vb应用程序来说是没有意义的


    cout<<ret<<endl;  //输出结果为2
    //或者使用$err,hr在监视里查看错误码和错误消息(系统里找不到指定的文件  2)

    system("pause");
    return 0;
}

(3).返回一个合法值,让程序处于某种非法的状态(坑爹的atoi())

atoi() 函数用来将字符串转换成整数(int),
其原型为: int atoi (const char * str);

【函数说明】atoi() 函数会扫描参数 str 字符串,跳过前面的空白字符(例如空格,tab缩进等,可以通过 isspace()
函数来检测),直到遇上数字或正负符号才开始做转换,而再遇到非数字或字符串结束时(‘\0’)才结束转换,并将结果返回。

【返回值】返回转换后的整型数;如果 str 不能转换成 int 或者 str 为空字符串,那么将返回 0。

4.调用一个预先准备好在出现错误的情况下用的函数(回调函数)

5.暴力解决方式(abort()或则exit())

6.使用goto语句(缺点:打乱程序的正常执行,跳过一下变量的定义、不允许函数间的跳转)

7.setjmp()和longjmp()

setjmp函数有何作用?

  setjmp是C标准库中提供的一个函数,它的作用是保存程序当前运行的一些状态。它的函数原型如下: int setjmp( jmp_buf env );
  MSDN中的评论,如下:
  setjmp函数用于保存程序的运行时的堆栈环境,接下来的其它地方,你可以通过调用longjmp函数来恢复先前被保存的程序堆栈环境。当 setjmp和longjmp组合一起使用时,它们能提供一种在程序中实现“非本地局部跳转”(”non-local goto”)的机制。并且这种机制常常被用于来实现,把程序的控制流传递到错误处理模块之中;或者程序中不采用正常的返回(return)语句,或函数的 正常调用等方法,而使程序能被恢复到先前的一个调用例程(也即函数)中。
  对setjmp函数的调用时,会保存程序当前的堆栈环境到env参数中;接下来调用longjmp时,会根据这个曾经保存的变量来恢复先前的环 境,并且当前的程序控制流,会因此而返回到先前调用setjmp时的程序执行点。此时,在接下来的控制流的例程中,所能访问的所有的变量(除寄存器类型的 变量以外),包含了longjmp函数调用时,所拥有的变量。

longjmp函数有何作用?

  同样,longjmp也是C标准库中提供的一个函数,它的作用是用于恢复程序执行的堆栈环境,它的函数原型如下:
void longjmp( jmp_buf env, int value );
  这是MSDN中的评论,如下:
  longjmp函数用于恢复先前程序中调用的setjmp函数时所保存的堆栈环境。setjmp和longjmp组合一起使用时,它们能提供一 种在程序中实现“非本地局部跳转”(”non-local goto”)的机制。并且这种机制常常被用于来实现,把程序的控制流传递到错误处理模块,或者不采用正常的返回(return)语句,或函数的正常调用等 方法,使程序能被恢复到先前的一个调用例程(也即函数)中。
  对setjmp函数的调用时,会保存程序当前的堆栈环境到env参数中;接下来调用longjmp时,会根据这个曾经保存的变量来恢复先前的环境,并且因此当前的程序控制流,会返回到先前调用setjmp时的执行点。此时,value参数值会被setjmp函数所返回,程序继续得以执行。并且, 在接下来的控制流的例程中,它所能够访问到的所有的变量(除寄存器类型的变量以外),包含了longjmp函数调用时,所拥有的变量;而寄存器类型的变量 将不可预料。setjmp函数返回的值必须是非零值,如果longjmp传送的value参数值为0,那么实际上被setjmp返回的值是1。

#include<iostream>
using namespace std;
#include<setjmp.h>

jmp_buf buf;//定义全局变量buf,保存当前信息的执行点
void FunTest1()
{
    longjmp(buf, 1);
}

void FunTest2()
{
    longjmp(buf, 2);
}

void FunTest3()
{
    longjmp(buf, 3);
}

int main()
{
    int iState = setjmp(buf);
    if(iState == 0)
    {
        FunTest1(); //一旦longjmp()函数跳到执行点后,后面的将不执行
        FunTest2();
        FunTest3();
    }
    else
    {
        switch(iState)
        {
        case 1:
            cout<<"FunTest1()"<<endl;//验证setjmp()和longjmp()
            break;
        case 2:
            cout<<"FunTest2()"<<endl;
            break;
        case 3:
            cout<<"FunTest3()"<<endl;
            break;
        }
    }
    system("pause");//打印FunTest1()
    return 0;
}

注意:
  在调用setjmp的函数返回之前,调用longjmp,否则结果不可预料。

#include<iostream>
using namespace std;
#include<setjmp.h>

jmp_buf buf;//定义全局变量buf,保存当前信息的执行点
void FunTest1()
{
    longjmp(buf, 1);
}

void FunTest2()
{
    longjmp(buf, 2);
}

void FunTest3()
{
    longjmp(buf, 3);
}

int main()
{
    int iState = 0;
    if(iState == 0)
    {
        FunTest1(); //一旦longjmp()函数跳到执行点后,后面的将不执行
        FunTest2();
        FunTest3();
    }
    else
    {
        switch(iState)
        {
        case 1:
            cout<<"FunTest1()"<<endl;//验证setjmp()和longjmp()
            break;
        case 2:
            cout<<"FunTest2()"<<endl;
            break;
        case 3:
            cout<<"FunTest3()"<<endl;
            break;
        }
    }
    system("pause");//程序崩溃,调用longjmp之前先调用setjmp,否则出现问题
    return 0;
}

 在使用longjmp时,请遵守以下规则或限制:

  · 不要假象寄存器类型的变量将总会保持不变。在调用longjmp之后,通过setjmp所返回的控制流中,例程中寄存器类型的变量将不会被恢复。
  · 不要使用longjmp函数,来实现把控制流,从一个中断处理例程中传出,除非被捕获的异常是一个浮点数异常。在后一种情况下,如果程序通过调用_fpreset函数,来首先初始化浮点数包后,它是可以通过longjmp来实现从中断处理例程中返回。
 

 总结:

  与goto语句不同,在C语言中,setjmp()与longjmp()的组合调用,为程序员提供了一种更优雅的异常处理机制。它具有如下特点:
  (1) goto只能实现本地跳转,而setjmp()与longjmp()的组合运用,能有效的实现程序控制流的非本地(远程)跳转;
  (2) 与goto语句不同,setjmp()与longjmp()的组合运用,提供了真正意义上的异常处理机制。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值