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;
}

我们看看编译结果

执行的结果是正确的,但是如果我们打印的是 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;
}

我们来看看编译结果

再来试试 0/1 呢

我们看到结果已经正确的执行了。但是这个程序有个缺陷,便是 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...,我们来看看编译结果

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

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

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

原文地址:http://blog.51cto.com/12810168/2124290

时间: 2024-11-05 13:03:54

C 语言异常处理(五十二)的相关文章

QT开发(五十二)———QML语言

QT开发(五十二)---QML语言 QML是一种声明语言,用于描述程序界面.QML将用户界面分解成一块块小的元素,每一元素都由很多组件构成.QML定义了用户界面元素的外观和行为:更复杂的逻辑则可以结合JavaScript脚本实现. 一.QML基础语法 1.Import语句 QML代码中,import语句一般写在头几行,主要用途如下:     A.包含类型的全名空间     B.包含QML代码文件的目录     C.JavaScript代码文件 格式如下: import Namespace Ver

Go语言开发(十二)、Go语言常用标准库二

Go语言开发(十二).Go语言常用标准库二 一.os 1.os简介 os 包提供了不依赖平台的操作系统函数接口,设计像Unix风格,但错误处理是go风格,当os包使用时,如果失败后返回错误类型而不是错误数量. 2.os常用接口 func Hostname() (name string, err error) // Hostname返回内核提供的主机名 func Environ() []string // Environ返回表示环境变量的格式为"key=value"的字符串的切片拷贝 f

苹果新的编程语言 Swift 语言进阶(十二)--选项链

选项链是使用选项来查询和调用其属性.方法或下标的一个过程,如果选项包含一个值,则属性.方法.下标的查询和调用成功,否则,调用返回nil. 选项链能用在任何类型的选项来检查对其一个属性.方法.下标的查询和调用是否成功. 选项链可以作为强制展开的替代方式使用,但选项链的使用更加安全,不会触发一个运行时错误. 在调用一个选项的属性.方法或下标方法时,通过在该选项值的后面放置一个(?)标记来规定一个选项链.这与在选项值后放置一个(!) 来强制展开选项的值非常相似.主要的不同是在选项值为nil时选项链能够

C++语言学习(十二)——C++语言常见函数调用约定

C++语言学习(十二)--C++语言常见函数调用约定 一.C++语言函数调用约定简介 C /C++开发中,程序编译没有问题,但链接的时候报告函数不存在,或程序编译和链接都没有错误,但只要调用库中的函数就会出现堆栈异常等现象.上述现象出现在C和C++的代码混合使用的情况下或在C++程序中使用第三方库(非C++语言开发)的情况下,原因是函数调用约定(Calling Convention)和函数名修饰(Decorated Name)规则导致的.函数调用约定决定函数参数入栈的顺序,以及由调用者函数还是被

第五十二个知识点:选择一个先进的应用概念,如电子投票,拍卖或多方计算。这样一个系统的大致安全需求是什么

第五十二个知识点:选择一个先进的应用概念,如电子投票,拍卖或多方计算.这样一个系统的大致安全需求是什么 这是我们认为每个密码学博士一年级都应该知道的52件事中的最后一件.你可能已经收集了过去的52个博客,我们希望学生知道从理论到实践的各个方面.但关键是你需要在密码学中考虑的不仅是对遵守规则的玩家的安全,还有对不遵守规则的玩家的安全.让我们从投票.拍卖和多方计算的角度来研究这个问题. 让我们先讨论一下三个应用程序的含义. 在投票中,我们根绝投票者进行一些投票方案(得票最多者当选.多选.赞成投票或其

数据结构算法C语言实现(十二)--- 3.4循环队列&amp;队列的顺序表示和实现

一.简述 空队列的处理方法:1.另设一个标志位以区别队列是空还是满:2.少用一个元素空间,约定以队列头指针在队尾指针下一位置上作为队列呈满的状态的标志. 二.头文件 1 //3_4_part1.h 2 /** 3 author:zhaoyu 4 email:[email protected] 5 date:2016-6-9 6 note:realize my textbook <<数据结构(C语言版)>> 7 */ 8 //Page 64 9 #include <cstdio

JavaWeb学习总结(五十二)——使用JavaMail创建邮件和发送邮件

一.RFC882文档简单说明 RFC882文档规定了如何编写一封简单的邮件(纯文本邮件),一封简单的邮件包含邮件头和邮件体两个部分,邮件头和邮件体之间使用空行分隔. 邮件头包含的内容有: from字段  --用于指明发件人 to字段      --用于指明收件人 subject字段  --用于说明邮件主题 cc字段     -- 抄送,将邮件发送给收件人的同时抄送给另一个收件人,收件人可以看到邮件抄送给了谁 bcc字段   -- 密送,将邮件发送给收件人的同时将邮件秘密发送给另一个收件人,收件人

JAVA学习第五十二课 — IO流(六)File对象

File类 用来给文件或者文件夹封装成对象 方便对文件与文件夹的属性信息进行操作 File对象可以作为参数传递给流的构造函数 一.构造函数和分隔符 public static void FileDemo() {//构造函数演示 //可以将一个已存在或不存在的文件或目录封装成File对象 File file = new File("d:\\a.txt"); File file2 = new File("d:","a.txt"); File file

广东海洋大学 电子1151 孔yanfei python语言程序设计 第十二周

八.SVM的实现之SMO算法 终于到SVM的实现部分了.那么神奇和有效的东西还得回归到实现才可以展示其强大的功力.SVM有效而且存在很高效的训练算法,这也是工业界非常青睐SVM的原因. 也就是说找到一组αi可以满足上面的这些条件的就是该目标的一个最优解.所以我们的优化目标是找到一组最优的αi*.一旦求出这些αi*,就很容易计算出权重向量w*和b,并得到分隔超平面了. 这是个凸二次规划问题,它具有全局最优解,一般可以通过现有的工具来优化.但当训练样本非常多的时候,这些优化算法往往非常耗时低效,以致