浅析C++编译器怎样实现异常处理问题

开发 后端
与传统语言相比,C++的一项革命性创新就是它支持异常处理。传统的错误处理方式经常满足不了要求,而异常处理则是一个极好的替代解决方案。它将正常代码 和错误处理代码清晰的划分开来,程序变得非常干净并且容易维护。

在介绍C++编译器如何实现异常处理的问题之前,先让大家了解下什么是C++编译器?其实C++编译器是一个与标准化C++高度兼容的编译环境,不同的编译器也会对不同的CPU进行不同的优化。

本文讨论了C++编译器如何实现异常处理。我将假定你已经熟悉异常处理的语法和机制,用于VC++的异常处理库,要用库中的处理程序替换掉VC++提供的那个,你只需要调用下面这个函数:

  1. install_my_handler(); 

之后,程序中的所有异常,从它们被抛出到堆栈展开(stack unwinding),再到调用catch块,***到程序恢复正常运行,都将由我的异常处理库来管理。

与其它C++特性一样,C++标准并没有规定编译器应该如何来实现异常处理。这意味着每一个编译器的提供商都可以用它们认为恰当的方式来实现它。下面我会 描述一下VC++是怎么做的,但即使你使用其它的编译器或操作系统①,本文也应该会是一篇很好的学习材料。VC++的实现方式是以windows系统的结 构化异常处理(SEH)②为基础的。

我认为C++编译器异常或者是被明确的抛出的,或者是由于除零溢出、空指针访问等引起的。当它发生时会产生一个中断,接下来控制权就会传递到操作系统 的手中。操作系统将调用异常处理程序,检查从异常发生位置开始的函数调用序列,进行堆栈展开和控制权转移。Windows定义了结构 "EXCEPTION_REGISTRATION",使我们能够向操作系统注册自己的异常处理程序。

  1. struct EXCEPTION_REGISTRATION  
  2. {  
  3.     EXCEPTION_REGISTRATION* prev;  
  4.     DWORD handler;  
  5. };  

注册时,只需要创建这样一个结构,然后把它的地址放到FS段偏移0的位置上去就行了。下面这句汇编代码演示了这一操作:

  1. mov FS:[0], exc_regp 

prev字段用于建立一个EXCEPTION_REGISTRATION结构的链表,每次注册新的EXCEPTION_REGISTRATION时,我们都要把原来注册的那个的地址存到prev中。那么,那个异常回调函数长什么样呢?在excpt.h中,windows定义了它的原形:

  1. EXCEPTION_DISPOSITION (*handler)(   
  2. _EXCEPTION_RECORD *ExcRecord,   
  3. void* EstablisherFrame,   
  4. _CONTEXT *ContextRecord,   
  5. void* DispatcherContext);  

不要管它的参数和返回值,我们先来看一个简单的例子。下面的程序注册了一个C++编译器异常处理程序,然后通过除以零产生了一个异常。异常处理程序捕获了它,打印了一条消息就完事大吉并退出了。

  1. #include   
  2. #include   
  3.  
  4. using std::cout;   
  5. using std::endl;   
  6.  
  7. struct EXCEPTION_REGISTRATION   
  8. {   
  9. EXCEPTION_REGISTRATION* prev;   
  10. DWORD handler;   
  11. };   
  12.  
  13. EXCEPTION_DISPOSITION myHandler(   
  14. _EXCEPTION_RECORD *ExcRecord,   
  15. void * EstablisherFrame,   
  16. _CONTEXT *ContextRecord,   
  17. void * DispatcherContext)   
  18. {   
  19. cout << "In the exception handler" << endl;   
  20. cout << "Just a demo. exiting..." << endl;   
  21. exit(0);   
  22. return ExceptionContinueExecution; //不会运行到这   
  23. }   
  24.  
  25. int  g_div = 0;   
  26.  
  27. void bar()   
  28. {   
  29. //初始化一个EXCEPTION_REGISTRATION结构   
  30. EXCEPTION_REGISTRATION reg, *preg = ®    
  31. reg.handler = (DWORD)myHandler;   
  32.  
  33. //取得当前异常处理链的"头"   
  34. DWORD prev;   
  35. _asm   
  36. {   
  37. mov EAX, FS:[0]   
  38. mov prev, EAX   
  39. }   
  40. reg.prev = (EXCEPTION_REGISTRATION*) prev;   
  41.  
  42. //注册!   
  43. _asm   
  44. {   
  45. mov EAX, preg   
  46. mov FS:[0], EAX   
  47. }   
  48.  
  49. //产生一个异常   
  50. int  j = 10 / g_div;  //异常,除零溢出   
  51. }   
  52.  
  53. int  main()   
  54. {   
  55. bar();   
  56. return 0;   
  57. }  

注意EXCEPTION_REGISTRATION必须定义在栈上,并且必须位于比上一个结点更低的内存地址上,windows对此有严格要求,达不到的话,它就会立刻终止进程。

【编辑推荐】

  1. 如何正确编写C++项目开发编写项目计划书
  2. 对C++库函数进行学习探索总结笔记
  3. 深度演示C++语言的种种高安全性
  4. 详细介绍如何准确无误的编写C++语言
  5. 深度演示C++语言的种种高安全性
责任编辑:chenqingxiang 来源: 天极
相关推荐

2010-01-22 18:33:17

C++编译器

2010-01-13 13:42:55

C++编译器

2010-01-27 16:39:48

C++编译器

2010-01-18 10:34:21

C++编译器

2010-01-08 16:00:46

C++编译器

2010-01-21 09:11:38

C++编译器

2010-02-23 17:23:26

Python异常处理

2010-01-18 10:28:15

C++编译器

2010-10-20 13:43:37

C++编译器

2010-01-20 11:15:38

CC++编译器

2010-02-03 13:14:03

C++编译器命令

2010-01-14 15:29:44

C++编译器

2015-03-23 10:04:43

c++编译器c++实现原理总结

2010-01-12 16:42:59

C++编译器

2010-01-21 09:26:53

CC++编译器

2010-01-14 14:55:14

C++编译器

2009-01-12 10:16:11

Visual C++编译器选项设置

2009-08-04 15:52:58

ASP.NET编译器

2009-07-06 12:49:33

JSP编译器

2010-01-27 14:48:55

优秀C++编译器
点赞
收藏

51CTO技术栈公众号