1,C 语言崇尚简洁高效,因此语言本身并没有异常处理的相关语法规则,但是异常处理在 C 语言中 是存在的,我们有必要从 C 语言开始先看一看 C 语言中的异常处理是怎样, 然后对比 C++ 里面的异常处理是怎样;

2,异常的概念:

1,程序在运行过程中可能产生异常;

1,异常是我们在程序开发中必须考虑的一些特殊情况;

2,异常(Exception)与 Bug 的区别:

1,异常是程序运行时可预料的执行分支;

1,例如计算器相关的程序,涉及到除法操作的时候,就应该全面的想想有没有会产生意外的情况,比如除数为 0,这是一种意外情况,但是我们已经预料到这种意外情况,并且这种意外情况在计算器相关的程序里面是不可避免的,因此我们在做开发的时候,就必须有一些代码来处理这样的情况,这些代码就叫做异常处理的代码,而异常是程序运行时可以预料的情况(这就是异常和 Bug 的区别);

2,Bug 是程序中的错误,是不被预期的运行方式;

3,异常和 Bug 的对比:

1,异常:

1,运行时产生除 0 的情况;

2,需要打开的外部文件不存在;

3,数组访问的越界;

2,Bug:

1,使用野指针;

2,堆数组使用结束后未释放;

1,内存泄漏一定是 Bug;

3,选择排序无法处理长度为 0 的数组;

4,异常处理的方式:

1,C 语言经典处理方式:if ... else ...

1,代码示例:

 void func(...)
{
if( 判断是否产生异常 )
{
// 正常情况代码逻辑;
}
else
{
// 异常情况代码逻辑;
}
}

2,除法操作异常处理编程实验:

 #include <iostream>
#include <string> using namespace std; double divide(double a, double b, int* valid)
{
const double delta = 0.000000000000001;
double ret = ; if( !((-delta < b) && (b < delta)) )
{
ret = a / b; *valid = ; // 为了处理除数为 0 和本身结果就是 0 之间的区别;
}
else
{
*valid = ; // 异常了;
} return ret;
} int main(int argc, char *argv[])
{
int valid = ;
double r = divide(, , &valid); if( valid )
{
cout << "r = " << r << endl;
}
else
{
cout << "Divided by zero..." << endl;
} return ;
}

1,工程上 if 处理正常分支,else 处理异常分支;

2,除法操作需要三个参数,而加减乘法都只要两个参数,这样不优雅;

3,如果第三个参数没有指向合法的对象(NULL),就会出现段错误;

3,缺陷:

1,divide 函数有 3 个参数,难以理解其用法;

1,降低了代码的可读性,后期的维护成本就会增加;

2,改进的方向就是统一除法和加减乘法的调用方式,将三个参数改变为两个参数,并且还有异常处理的代码;

2,divide 函数调用后必须判断 valid 代表的结果;

1,当 valid 为 true 时,运行结果正常;

2,当 valid 为 false 时,运行过程出现异常;

4,通过 setjmp() 和 longjmp() 进行优化:

1,int setjmp(jmp_buf env)

1,将当前上下文保存在 jmp_buf 结构体中;

2,jmp_buf 一般的是全局变量;

2,void longjmp(jmp_buf env, int val)

1,从 jmp_buf 结构体中恢复 setjmp() 保存的上下文;

2,最终从 setjmp 函数调用点返回,返回值为 val;

3,这两个函数不要随便调用,因为它们简单粗暴,它们将破坏结构化程序设计的特性,结构化程序设计三大特性有顺序执行、循环执行、分支执行,如果调用这两个函数,将会有其他的执行方式出来;

5,除法操作异常处理优化编程实验:

 #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 = ; if( !((-delta < b) && (b < delta)) )
{
ret = a / b;
}
else
{
longjmp(env, ); // 当 b = 0 时,跳转回 env 这个全局变量所保存的程序上下文的地方,也就是 main() 中的 if() 语句;此时 divide() 程序的执行立即停止,并从 setjmp() 函数返回,返回值为 longjmp() 中的参数 1;
} return ret;
} int main(int argc, char *argv[])
{
if( setjmp(env) == ) //调用 setjmp(env) 将程序的上下文保存在 env 里面;
{
double r = divide(, ); // Divided by zero...;
double r = divide(, ); // 1; cout << "r = " << r << endl;
}
else
{
cout << "Divided by zero..." << endl;
} return ;
}

1,程序执行过程:

1,首先执行到 main() 中的 if() 语句;

2,直接调用 setjmp(),这个函数内部就会将当前程序执行的上下文全部保存在这个全局变量里面,因为是直接调用的,所以保存完了返回值就是 0;

3,运行到 divide() 后,判断到 b 为 0,则执行 longjum(),它根据 env 这个全局变量里面保存的信息来恢复之前的程序上下文;

4,之前的程序上下文是在 if() 中调用 setjmp() 保存的,所以说  longjmp() 函数一旦被调用,则 divide() 函数立即停止,出口在 setjmp(),此时返回值是 longjmp() 中第二个参数 1;

5,执行到 if...else 语句的 else 语句中;

2,这两个函数配合起来破坏了结构化程序设计的三大特性,所以简单的程序理解起来很复杂;

3,优雅但理解难,工程应用不多,工程中推荐使用第一种异常处理方式;

6,改进后缺陷:

1,setjmp() 和 longjmp() 的引入:

1,必然涉及到使用全局变量;

2,暴力跳转导致代码可读性降低;

3,本质还是 if ... else ... 异常处理方式;

7,C 语言中的经典异常处理方式问题:

1,会使得编程中逻辑中混入大量的处理异常代码;

2,正常逻辑代码和异常处理代码混合在一起,导致代码迅速膨胀,难以维护;

1,工程中约定,if 处理正常情况,else 处理异常情况,好歹代码中能够看出正常异常处理代码放在什么地方;

5,异常处理代码分析实例分析:

 #include <iostream>
#include <string> using namespace std; #define SUCCESS 0
#define INVALID_POINTER -1
#define INVALID_LENGTH -2
#define INVALID_PARAMETER -3 int MemSet(void* dest, unsigned int length, unsigned char v)
{
if( dest == NULL )
{
return INVALID_POINTER;
} if( length < )
{
return INVALID_LENGTH;
} if( (v < ) || (v > ) )
{
return INVALID_PARAMETER;
} unsigned char* p = (unsigned char*)dest; for(int i=; i<length; i++)
{
p[i] = v;
} return SUCCESS;
} int main(int argc, char *argv[])
{
int ai[];
int ret = MemSet(ai, sizeof(ai), ); if( ret == SUCCESS ) // 处理异常代码逻辑;
{
}
else if( ret == INVALID_POINTER ) // 三个地方处理异常代码逻辑;
{
}
else if( ret == INVALID_LENGTH )
{
}
else if( ret == INVALID_PARAMETER )
{
} return ret;
}

1,这对于职场新人很痛苦,因为代码逻辑的模块不清晰;

6,C++ 中有没有更好的异常处理方式?

1,直接通过关键字知道哪些代码处理正常情况的代码,哪些是异常情况的代码;

7,小结:

1,程序中不可避免的会发生异常;

2,异常是在开发阶段就可以预见的运行时问题;

3,C 语言中通过经典的 if ... else ... 方式处理异常;

4,C++ 中存在更好的异常处理方式;

C中的异常处理的更多相关文章

  1. 【repost】JS中的异常处理方法分享

    我们在编写js过程中,难免会遇到一些代码错误问题,需要找出来,有些时候怕因为js问题导致用户体验差,这里给出一些解决方法 js容错语句,就是js出错也不提示错误(防止浏览器右下角有个黄色的三角符号,要 ...

  2. 第65课 C++中的异常处理(下)

    1. C++中的异常处理 (1)catch语句块可以抛出异常 ①catch中获捕的异常可以被重新抛出 ②抛出的异常需要外层的try-catch块来捕获 ③catch(…)块中抛异常的方法是throw; ...

  3. Swift基础--Swift中的异常处理

    Swift中的异常处理 OC中的异常处理:方法的参数要求传入一个error指针地址,方法执行完后,如果有错误,内部会给error赋值 Swift中的异常处理:有throws的方法,就要try起来,然后 ...

  4. ASP.NET Web API 中的异常处理(转载)

    转载地址:ASP.NET Web API 中的异常处理

  5. Struts2中的异常处理

    因为在Action的execute方法声明时就抛出了Exception异常,所以我们无需再execute方法中捕捉异常,仅需在struts.xml 中配置异常处理. 为了使用Struts2的异常处理机 ...

  6. C++中的异常处理(三)

    C++中的异常处理(三) 标签: c++C++异常处理 2012-11-24 23:00 1520人阅读 评论(0) 收藏 举报  分类: 编程常识(2)  版权声明:本文为博主原创文章,未经博主允许 ...

  7. C++中的异常处理(二)

    C++中的异常处理(二) 标签: c++C++异常处理 2012-11-24 20:56 1713人阅读 评论(2) 收藏 举报  分类: C++编程语言(24)  版权声明:本文为博主原创文章,未经 ...

  8. C++中的异常处理(一)

     来自:CSDN 卡尔  后续有C++中的异常处理(二)和C++中的异常处理(三),C++中的异常处理(二)是对动态分配内存后内部发生错误情况的处理方法,C++中的异常处理(三)中是使用时的异常说明. ...

  9. Java中实现异常处理的基础知识

    Java中实现异常处理的基础知识 异常 (Exception):发生于程序执行期间,表明出现了一个非法的运行状况.许多JDK中的方法在检测到非法情况时,都会抛出一个异常对象. 例如:数组越界和被0除. ...

  10. java 中的异常处理

    一. 异常的概念和Java异常体系结构  异常是程序运行过程中出现的错误.本文主要讲授的是Java语言的异常处理.Java语言的异常处理框架,     是Java语言健壮性的一个重要体现. Java把 ...

随机推荐

  1. [JZOJ6247]【NOI2019模拟2019.6.27】C【计数】

    Description n<=200000 Solution 比赛时没做出这道题真的太弟弟了 首先我们从小到大插入数i,考虑B中有多少个区间的最大值为i 恰好出现的次数不太好计算,我们考虑计算最 ...

  2. 【WC2016】论战捆竹竿

    已经快三周了啊--终于把挖的坑填了-- 首先显然是把除了自身的所有border拿出来,即做 \(\left\{ n - b_1, n - b_2, \dots, n - b_k, n \right\} ...

  3. unittest详解(六) 断言

    我们在执行测试用例时,怎么来判断这条用例是否通过呢?唯一的办法就是拿实际结果和预期结果进行比较,如果一致用例就是通过的,否则用例就是失败的.在python中这种比较的方法就叫做断言,unittest框 ...

  4. R_Studio(学生成绩)对数据进行属性构造处理

    对“Gary.csv”中数据进行进行属性构造处理,增加“总成绩”属性 Gary.csv setwd('D:\\data') list.files() #数据读取 dat=read.csv(file=& ...

  5. Java容器——Set和顺序存储

    当Set使用自己创建的类型时,存储的顺序如何维护,在不同的Set实现中会有不同,而且它们对于在特定的Set中放置的元素类型也有不同的要求: Set(interface) 存入Set的每个元素都必须是唯 ...

  6. LeetCode124----二叉树中最大路径和

    给定一个非空二叉树,返回其最大路径和. 本题中,路径被定义为一条从树中任意节点出发,达到任意节点的序列.该路径至少包含一个节点,且不需要经过根节点. 示例 1: 输入: [1,2,3] 1 / \ 2 ...

  7. ctf活动结果

    签到题:flag{0ca175b9c0f726831d895e269332461} 解题过程:使用winhex打开图片,查询到ANSI ASCII 得到结果 1.SimCTF{hello simple ...

  8. Daily Schedule

    8.29~9.2周报: 完成Forward Path页面测试用例 2天 完成当前Return Path可执行控件的相关测试用例 1.5天 整理更新web自动化测试用例文档 1天 跟踪定位SNMP自动化 ...

  9. 源码编译apache出错

    报错信息如下 exports.c:1572: error: redefinition of `ap_hack_apr_allocator_create' exports.c:177: error: ` ...

  10. Android中@id与@+id区别和sharedUserId属性详解

    Android中的组件需要用一个int类型的值来表示,这个值也就是组件标签中的id属性值. id属性只能接受资源类型的值,也就是必须以@开头的值,例如,@id/abc.@+id/xyz等. 如果在@后 ...