当前位置: 首页 > 图文教程 > 开发语言 > VC++ > C++编译器如何实现异常处理

VC++
在类VC的界面实现中加入目录树
软件换肤技术在 BCB 中的实现
利用非模窗口生成MDI介面
报表输出轻松搞定
Windows 中不规则窗体的编程实现
解说Win32的窗口子类化
使用测试优先方法开发用户界面
一个简单的登录对话框的实现
一个简单的日记本程序
从资源中加载皮肤
一个在RichEdit中添加表情图象的类
ActiveSkin 4.3 软件换肤在VC中的实现
一种另类“关于(About)”对话框的动态显示方法
对话框打印预览及打印
关于如何换肤、子类化的解决方案
制作 MSN、QQ 的消息提示窗口
如何对 BCGControlBarPro 进行换肤
定制个性化的对话框窗口类
改变窗口中的光标形状
更新MFC中的视图,跟踪.NET Framework中的事件

VC++ 中的 C++编译器如何实现异常处理


出处:互联网   整理: 软晨网(RuanChen.com)   发布: 2009-10-30   浏览: 77 ::
收藏到网摘: n/a

C++编译器如何实现异常处理

原著:Vishal Kochhar
翻译:局部变量

原文出处:How a C++ compiler implements exception handling

译者注:本文在网上已经有几个译本,但都不完整,所以我决定自己把它翻译过来。虽然力求信、雅、达,但鉴于这是我的第一次翻译经历,不足之处敬请谅解并指出。
  与传统语言相比,C++的一项革命性创新就是它支持异常处理。传统的错误处理方式经常满足不了要求,而异常处理则是一个极好的替代解决方案。它将正常代码和错误处理代码清晰的划分开来,程序变得非常干净并且容易维护。本文讨论了编译器如何实现异常处理。我将假定你已经熟悉异常处理的语法和机制。本文还提供了一个用于VC++的异常处理库,要用库中的处理程序替换掉VC++提供的那个,你只需要调用下面这个函数:

install_my_handler();

  之后,程序中的所有异常,从它们被抛出到堆栈展开(stack unwinding),再到调用catch块,最后到程序恢复正常运行,都将由我的异常处理库来管理。
  与其它C++特性一样,C++标准并没有规定编译器应该如何来实现异常处理。这意味着每一个编译器的提供商都可以用它们认为恰当的方式来实现它。下面我会描述一下VC++是怎么做的,但即使你使用其它的编译器或操作系统①,本文也应该会是一篇很好的学习材料。VC++的实现方式是以windows系统的结构化异常处理(SEH)②为基础的。

结构化异常处理—概述

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

struct EXCEPTION_REGISTRATION{ EXCEPTION_REGISTRATION* prev; DWORD handler;}; 
  注册时,只需要创建这样一个结构,然后把它的地址放到FS段偏移0的位置上去就行了。下面这句汇编代码演示了这一操作:
mov FS:[0], exc_regp
  prev字段用于建立一个EXCEPTION_REGISTRATION结构的链表,每次注册新的EXCEPTION_REGISTRATION时,我们都要把原来注册的那个的地址存到prev中。
那么,那个异常回调函数长什么样呢?在excpt.h中,windows定义了它的原形:
EXCEPTION_DISPOSITION (*handler)( _EXCEPTION_RECORD *ExcRecord, void* EstablisherFrame, _CONTEXT *ContextRecord, void* DispatcherContext); 
  不要管它的参数和返回值,我们先来看一个简单的例子。下面的程序注册了一个异常处理程序,然后通过除以零产生了一个异常。异常处理程序捕获了它,打印了一条消息就完事大吉并退出了。
#include <iostream> #include <windows.h> using std::cout; using std::endl; struct EXCEPTION_REGISTRATION { EXCEPTION_REGISTRATION* prev; DWORD handler; }; EXCEPTION_DISPOSITION myHandler( _EXCEPTION_RECORD *ExcRecord, void * EstablisherFrame, _CONTEXT *ContextRecord, void * DispatcherContext) { cout << "In the exception handler" << endl; cout << "Just a demo. exiting..." << endl; exit(0); return ExceptionContinueExecution; //不会运行到这 } int g_div = 0; void bar() { //初始化一个EXCEPTION_REGISTRATION结构 EXCEPTION_REGISTRATION reg, *preg = ® reg.handler = (DWORD)myHandler; //取得当前异常处理链的“头” DWORD prev; _asm { mov EAX, FS:[0] mov prev, EAX } reg.prev = (EXCEPTION_REGISTRATION*) prev; //注册! _asm { mov EAX, preg mov FS:[0], EAX } //产生一个异常 int j = 10 / g_div; //异常,除零溢出 } int main() { bar(); return 0; } /*-------输出------------------- In the exception handler Just a demo. exiting... ---------------------------------*/
  注意EXCEPTION_REGISTRATION必须定义在栈上,并且必须位于比上一个结点更低的内存地址上,Windows对此有严格要求,达不到的话,它就会立刻终止进程。


函数和堆栈

  
堆栈是用来保存局部对象的连续内存区。更明确的说,每个函数都有一个相关的栈桢(stack frame)来保存它所有的局部对象和表达式计算过程中用到的临时对象,至少理论上是这样的。但现实中,编译器经常会把一些对象