C/C++异常处理机制

1.C语言异常处理 

   1.1 异常终止 

   标准C库提供了abort()和exit()两个函数,它们可以强行终止程序的运行,其声明处于<stdlib.h>头文件中。这两个函数本身不能检测异常,但在C程序发生异常后经常使用这两个函数进行程序终止。下面的这个例子描述了exit()的行为:

#include <stdio.h>

#include <stdlib.h>

int main(void)

{

  exit(EXIT_SUCCESS);

  printf("程序不会执行到这里\n");

  return 0;

}

   在这个例子中,main函数一开始就执行了exit函数(此函数原型为void exit(int)),因此,程序不会输出"程序不会执行到这里"。程序中的exit(EXIT_SUCCESS)表示程序正常结束,与之对应的exit(EXIT_FAILURE)表示程序执行错误,只能强行终止。EXIT_SUCCESS、EXIT_FAILURE分别定义为0和1。

   对于exit函数,我们可以利用atexit函数为exit事件"挂接"另外的函数,这种"挂接"有点类似Windows编程中的"钩子"(Hook)。譬如:

#include <stdio.h>

#include <stdlib.h>

static void atExitFunc(void)

{

  printf("atexit挂接的函数\n");

}

int main(void)

{

  atexit(atExitFunc);

  exit(EXIT_SUCCESS);

  printf("程序不会执行到这里\n");

  return 0;

}

   程序输出"atexit挂接的函数"后即终止。来看下面的程序,我们不调用exit函数,看看atexit挂接的函数会否执行:

#include <stdio.h>

#include <stdlib.h>

static void atExitFunc(void)

{

  printf("atexit挂接的函数\n");

}

int main(void)

{

  atexit(atExitFunc);

  //exit(EXIT_SUCCESS);

  printf("不调用exit函数\n");

  return 0;

}

   程序输出:

   不调用exit函数

   atexit挂接的函数

   这说明,即便是我们不调用exit函数,当程序本身退出时,atexit挂接的函数仍然会被执行。 

   atexit可以被多次执行,并挂接多个函数,这些函数的执行顺序为后挂接的先执行,例如:

#include <stdio.h>

#include <stdlib.h>

static void atExitFunc1(void)

{

  printf("atexit挂接的函数1\n");

}

static void atExitFunc2(void)

{

  printf("atexit挂接的函数2\n");

}

static void atExitFunc3(void)

{

  printf("atexit挂接的函数3\n");

}

int main(void)

{

  atexit(atExitFunc1);

  atexit(atExitFunc2);

  atexit(atExitFunc3);

  return 0;

}

   输出的结果是:

    atexit挂接的函数3

    atexit挂接的函数2

    atexit挂接的函数1

   在Visual C++中,如果以abort函数(此函数不带参数,原型为void abort(void))终止程序,则会在debug模式运行时弹出DEBUG调试对话框。

1.2 断言(assert) 

   assert宏在C语言程序的调试中发挥着重要的作用,它用于检测不会发生的情况,表明一旦发生了这样的情况,程序就实际上执行错误了,例如strcpy函数:

char *strcpy(char *strDest, const char *strSrc)

{

  char * address = strDest;

  assert((strDest != NULL) && (strSrc != NULL));

  while ((*strDest++ = *strSrc++) != ‘\0‘)  ;

  return address;

}

   其中包含断言assert( (strDest != NULL) && (strSrc != NULL) ),它的意思是源和目的字符串的地址都不能为空,一旦为空,程序实际上就执行错误了,会引发一个abort。

   assert宏的定义为: 

#ifdef NDEBUG

#define assert(exp) ((void)0)

#else

#ifdef __cplusplus

extern "C"

{

  #endif

  _CRTIMP void __cdecl _assert(void *, void *, unsigned);

  #ifdef __cplusplus

}

#endif

#define assert(exp) (void)( (exp) || (_assert(#exp, __FILE__, __LINE__), 0) )

#endif /* NDEBUG */

   如果程序不在debug模式下,assert宏实际上什么都不做;而在debug模式下,实际上是对_assert() 函数的调用,此函数将输出发生错误的文件名、代码行、条件表达式。例如下列程序: 

#include <stdio.h>

#include <stdlib.h>

#include <assert.h>

char * myStrcpy( char *strDest, const char *strSrc )

{

  char *address = strDest;

  assert( (strDest != NULL) && (strSrc != NULL) );

  while( (*strDest++ = *strSrc++) != ‘\0‘ );

   return address;

}

int main(void)

{

  myStrcpy(NULL,NULL);

  return 0;

}

在此程序中,为了避免我们的strcpy与C库中的strcpy重名,将其改为myStrcpy。 断言失败,这是因为_assert()函数中也调用了abort()函数。

 一定要记住的是assert本质上是一个宏,而不是一个函数,因而不能把带有副作用的表达式放入assert"参数"中。 

1.3 errno 

   errno在C程序中是一个全局变量,这个变量由C运行时库函数设置,用户程序需要在程序发生异常时检测之。C运行库中主要在math.h和stdio.h头文件声明的函数中使用了errno,前者用于检测数学运算的合法性,后者用于检测I/O操作中(主要是文件)的错误,例如:

#include <errno.h>

#include <math.h>

#include <stdio.h>

int main(void)

{

  errno = 0;

  if (NULL == fopen("d:\\1.txt", "rb"))

  {

   printf("%d", errno);

  }

  else

  {

   printf("%d", errno);

  }

  return 0;

}

在此程序中,如果文件打开失败(fopen返回NULL),证明发生了异常。我们读取error可以获知错误的原因,如果D盘根目录下不存在"1.txt"文件,将输出2,表示文件不存在;在文件存在并正确打开的情况下,将执行到else语句,输出0,证明errno没有被设置。

2.1 C++异常处理语法 

   感谢C++语言的后期改造者们,他们在标准C++语言中专门集成了异常处理的相关语法(与之不同的是,所有的C 标准库异常体系都需要运行库的支持,它不是语言内核支持的)。当然,异常处理被加到程序设计语言中,也是程序语言发展和逐步完善的必然结果。我们看到,C++不是唯一集成异常处理的语言。

   C++的异常处理结构为: 

try 

//可能引发异常的代码 

catch(type_1 e) 

// type_1类型异常处理 

catch(type_2 e) 

// type_2类型异常处理 

catch (...)//会捕获所有未被捕获的异常,必须最后出现 

}  

   而异常的抛出方式为使用throw(type e)trycatchthrow都是C++为处理异常而添加的关键字。

看看这个例子:

#include <stdio.h>

//定义Point结构体(类)

typedef struct tagPoint

{

  int x;

  int y;

} Point;

//扔出int异常的函数

static void f(int n)

{

  throw 1;

}

//扔出Point异常的函数

static void f(Point point)

{

  Point p;

  p.x = 0;

  p.y = 0;

  throw p;

}

int main()

{

  Point point;

  point.x = 0;

  point.y = 0;

  try

  {

   f(point); //抛出Point异常

   //f(1); //抛出int异常

  }

  catch (int e)

  {

   printf("捕获到int异常:%d\n", e);

  }

  catch (Point e)

  {

   printf("捕获到Point异常:(%d,%d)\n", e.x, e.y);

  }

  return 0;

}

函数f定义了两个版本:f(int)和f(Point),分别抛出int和Point异常。当main函数的try{…}中调用f(point)时和f(1)时,分别输出: 捕获到Point异常:(0,0)  和  捕获到int异常:1

C++中,throw抛出异常的特点有: 

   (1)可以抛出基本数据类型异常,如int和char等;

   (2)可以抛出复杂数据类型异常,如结构体(在C++中结构体也是类)和类;

   (3)C++的异常处理必须由调用者主动检查。一旦抛出异常,而程序不捕获的话,那么abort()函数就会被调用,弹出如图1所示的对话框,程序被终止;

   (4)可以在函数头后加throw([type-ID-list])给出异常规格,声明其能抛出什么类型的异常。type-ID-list是一个可选项,其中包括了一个或多个类型的名字,它们之间以逗号分隔。如果函数没有异常规格指定,则可以抛出任意类型的异常。

2.2 标准异常 

下面给出了C++提供的一些标准异常: 

namespace std

{

  //exception派生

  class logic_error; //逻辑错误,在程序运行前可以检测出来

  //logic_error派生

  class domain_error; //违反了前置条件

  class invalid_argument; //指出函数的一个无效参数

  class length_error; //指出有一个超过类型size_t的最大可表现值长度的对象的企图

  class out_of_range; //参数越界

  class bad_cast; //在运行时类型识别中有一个无效的dynamic_cast表达式

  class bad_typeid; //报告在表达试typeid(*p)中有一个空指针p

  //exception派生

  class runtime_error; //运行时错误,仅在程序运行中检测到

  //runtime_error派生

  class range_error; //违反后置条件

  class overflow_error; //报告一个算术溢出

  class bad_alloc; //存储分配错误

}

请注意观察上述类的层次结构,可以看出,标准异常都派生自一个公共的基类exception。基类包含必要的多态性函数提供异常描述,可以被重载。下面是exception类的原型:

class exception

{

  public:

   exception() throw();

   exception(const exception& rhs) throw();

   exception& operator=(const exception& rhs) throw();

   virtual ~exception() throw();

   virtual const char *wh;

};

关于标准C++的异常处理,还包含一些比较复杂的技巧和内容,我们可以查阅《more effective C++》的条款9~条款15。

时间: 2024-08-12 13:08:47

C/C++异常处理机制的相关文章

深入理解Java异常处理机制

1. 引子 try-catch-finally恐怕是大家再熟悉不过的语句了,而且感觉用起来也是很简单,逻辑上似乎也是很容易理解.不过,我亲自体验的"教训"告诉我,这个东西可不是想象中的那么简单.听话.不信?那你看看下面的代码,"猜猜"它执行后的结果会是什么?不要往后看答案.也不许执行代码看真正答案哦.如果你的答案是正确,那么这篇文章你就不用浪费时间看啦. <span style="background-color: rgb(255, 255, 255

Java异常处理机制的秘密

一.结论 这些结论你可能从未听说过,但其正确性是毋庸置疑的,不妨先看看: 1.catch中throw不一定能抛回到上一层,因为finally中的return会抑制这个throw2.finally中throw一定能抛回上一层,因为此时其后的return不会被执行到(throw中断了正常的顺序流)3.在try/catch中return并不会直接返回上一层,而是先执行finally再返回 二.一段小程序 ? 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19

C++学习笔记27:异常处理机制

一.异常处理机制基础 异常的定义 程序中可以检测的运行不正常的情况 异常处理的基本流程 某段程序代码在执行操作时发生特殊情况,引发一个特定的异常 另一段程序代码捕获该异常并处理它 二.异常的引发 throw 三.异常的捕获 try { ... } catch(...) 四.异常类与异常对象 五.异常处理策略 异常类可以派生和继承,形成类库架构 可捕获的异常对象的型式 普通型式(包括类):异常对象需要拷贝 对某型式对象的引用:没有额外的拷贝动作 指向某型式对象的指针:要求对象动态构造或者在catc

Java之异常处理机制

来源:深入理解java异常处理机制 2.Java异常    异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程.Java通 过API中Throwable类的众多子类描述各种不同的异常.因而,Java异常都是对象,是Throwable子类的实例,描述了出现在一段编码中的 错误条件.当条件生成时,错误将引发异常. Java异常类层次结构图: 图1 Java异常类层次结构图 在 Java 中,所有的异常都有一个共同的祖先 Th

基于返回值的错误处理与异常处理机制

基于返回的错误处理和异常处理机制的比较 一.基于返回值的错误处理机制 对于传统的面向过程语言比如说C语言,通常使用基于返回值的错误处理机制,即通过在程序中定义程序出错时的返回值,比如说0代表操作成功,而1代表操作失败. 这种错误处理机制的好处在于有时候即使出现了异常程序或许也可以继续执行(但是最终执行结果或许不对,而这就导致查错比较困难,因为有时候仅仅通过函数返回值我们并不知道程序出错的原因究竟是什么,比如说程序操作失败返回值为0,我们在控制台上看到程序输出为0,但是除此之外没有其他额外的信息帮

深入理解java的异常处理机制

 JAVA异常的概念    异常指不期而至的各种状况,如:文件找不到.网络连接失败.非法参数等.异常是一个事件,它发生在程序运行期间,干扰了正常的指令流程.Java通 过API中Throwable类的众多子类描述各种不同的异常.因而,Java异常都是对象,是Throwable子类的实例,描述了出现在一段编码中的 错误条件.当条件生成时,错误将引发异常. Java异常类层次结构图: 图1 Java异常类层次结构图 在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出).Thr

Java从零开始(3)异常处理机制

异常处理是程序设计中一个非常重要的方面,也是程序设计的一大难点,从C开始,你也许已经知道如何用if...else...来控制异常了,也许是自发的,然而这种控制异常痛苦,同一个异常或者错误如果多个地方出现,那么你每个地方都要做相同处理,感觉相当的麻烦! Java语言在设计的当初就考虑到这些问题,提出异常处理的框架的方案,所有的异常都可以用一个类型来表示,不同类型的异常对应不同的子类异常(这里的异常包括错误概念),定义异常处理的规范,在1.4版本以后增加了异常链机制,从而便于跟踪异常!这是Java语

java异常处理机制

/* * 异常处理机制 * 1.分类:Error和Exception * Error错误是JVM自动报错的,程序员无法解决例如开数组过大int a[]=new int [1024*1024*1024]; * Exception错误时程序员要解决的问题,例如指针越界,零做除数 * 2.异常处理 * try{ * 需要检测是否发生异常的代码 * } * catch(Exception e){ * 处理异常的代码(一般是打印错误信息) * } * finally{ * 必须要运行的代码(一般是关闭数据

C++中的异常处理机制

C++中的捕获异常机制catch参数中实参的类型不同,采取的处理方式则不相同,且与普通的函数调用还不一样,具体表现为当抛出异常throw A()或throw obj时,对象会进行一次额外的对象复制操作. 测试类实现如下: #include <iostream> /** * 测试异常抛出与虚函数 */ using namespace std; class A { public: A() {cout << "A() " << endl;} ~A(){co

Atitit.js javascript异常处理机制与java异常的转换.js exception process Voae

Atitit.js javascript异常处理机制与java异常的转换.js exception processVoae 1. 1. javascript异常处理机制 1 2. 2. Web前后台异常的统一处理 1 3.  java异常转换为js异常 1 3. -------------详细代码 2 1. 1. javascript异常处理机制 Throw str Not throw error  ..cause ie,ff error obj is diff.. 2. 2. Web前后台异常