C 语言异常处理(五十二)

        我们今天来看下异常处理,在看 C++ 的异常处理之前,先来看看 C 语言中的异常处理。那么什么是异常呢?在程序运行过程中可能会产生异常,异常(Exception)与 Bug 的区别是:异常是程序运行时可预料的执行分支,而 Bug 是程序中的错误,是不被预期的运行方式。

        下来我们来看看异常和 Bug 的对比:a> 异常比如运行时产生除 0 的情况,需要打开的外部文件不存在,数组访问时越界;b> Bug 是使用野指针,堆数组使用结束后未释放,选择排序无法处理长度为 0 的数组。在 C 语言中的经典处理方式为:if ... else ... 。if 语句中处理的是正常情况代码逻辑,else 语句中处理的是异常情况代码逻辑。

        我们还是以代码为例来看看除法操作异常的处理

#include <iostream>
#include <string>

using namespace std;

double divide(double a, double b)
{
    const double delta = 0.000000000000001;
    double ret = 0;
    
    if( !((-delta < b) && (b < delta)) )
    {
        ret = a / b;
    }
    else
    {
        ret = 0;
    }
    
    return ret;
}

int main()
{
    cout << divide(1, 1) << endl;
    cout << divide(1, 0) << endl;

    return 0;
}

        我们看看编译结果

图片.png

        执行的结果是正确的,但是如果我们打印的是 0/1 的结果呢?我们就不知道执行的到底是正确的情况还是异常的情况。那么我们在上面程序中的 divide 函数中添加一个参数,用来表示执行结果的正确与否,根据这个参数的值来判断执行是否正常。程序如下

#include <iostream>
#include <string>

using namespace std;

double divide(double a, double b, int* valid)
{
    const double delta = 0.000000000000001;
    double ret = 0;
    
    if( !((-delta < b) && (b < delta)) )
    {
        ret = a / b;
        
        *valid = 1;
    }
    else
    {
        *valid = 0;
    }
    
    return ret;
}

int main()
{
    int valid = 0;
    double r = divide(1, 0, &valid);
    
    if( valid )
    {
        cout << "r = " << r << endl;
    }
    else
    {
        cout << "Divided by zero..." << endl;
    }

    return 0;
}

        我们来看看编译结果

图片.png

        再来试试 0/1 呢

图片.png

        我们看到结果已经正确的执行了。但是这个程序有个缺陷,便是 divide 函数需要 3 个参数,难以理解它的用法,而且 divide 函数调用后必须判断 valid 代表的结果,当 valid 为 true 时,运算结果正常;当 valid 为false 时,运算结果出现异常。在 C 语言还有一种异常处理的方式,通过 setjmp() 和 longjmp() 进行判断。下来我们来讲讲这两个函数的原型及其意思:a> int setjmp(jmp_buf env) 是将当前上下文保存在 jmp_buf 结构体中;b> void longjmp(jmp_buf env, int val) 从 jmp_buf 结构体中恢复 setjmp() 保存的上下文,最终从 setjmp 函数调用点返回,返回值为 val;下来我们通过示例代码来进行分析

#include <iostream>
#include <string>
#include <csetjmp>

using namespace std;

static jmp_buf env;

double divide(double a, double b)
{
    const double delta = 0.000000000000001;
    double ret = 0;
    
    if( !((-delta < b) && (b < delta)) )
    {
        ret = a / b;
    }
    else
    {
        longjmp(env, 1);
    }
    
    return ret;
}

int main()
{
    if( setjmp(env) == 0 )
    {
        double r = divide(1, 0);
        
        cout << "r = " << r << endl;
    }
    else
    {
        cout << "Divided by zero..." << endl;
    }

    return 0;
}

        在它进入 main 函数的 if 语句后,会将 0 保存在当前的 env 中,然后 setjmp 函数会保存当前的上下文信息。然后执行 divide 函数,进入到 divide 函数中,会进入到 else 分支。longjmp 函数则会将 1 保存到 env 中并将程序的执行流跳转到 setjmp 处,此时 env 为 1,因此条件不成立,所以会打印出 Divided by zero...,我们来看看编译结果

图片.png

        我们再来看看 1/1 呢,进入到 divide 函数中,它会执行 if 语句进行正常的计算之后直接便会返回 ret,便会输出结果

图片.png

        虽然这是两个参数,但是它有一定的缺陷。setjmp() 和 longjmp() 的引入就必然涉及到使用全局变量,暴力跳转导致代码的可读性降低,其本质还是 if ... else ... 异常处理方式。它的暴力跳转会破坏 C 语言的结构化特性(顺序执行、选择执行、循环执行)。C 语言中的经典异常处理方式会使得程序中逻辑混入大量的处理异常的代码。正常逻辑代码和异常处理代码混合在一起,导致代码迅速膨胀,难以维护。那么在 C++ 中肯定便会有更好的异常处理方式,我们后面会继续学习。通过对 C 语言中异常处理的学习,总结如下:1、程序中不可避免的会发生异常;2、异常是在开发阶段就可以预见的运行时问题;3、C 语言中通过经典的 if ... else ... 方式处理异常,在 C++ 中存在更好的异常处理方式。


        欢迎大家一起来学习 C++ 语言,可以加我QQ:243343083

猜你喜欢

转载自blog.51cto.com/12810168/2124290