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

2016-01-29 12:26 16 1 收藏

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

【 tulaoshi.com - C语言心得技巧 】

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

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

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com)

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

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

(本文来源于图老师网站,更多请访问https://www.tulaoshi.com)
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)来保存它所有的局部对象和表达式计算过程中用到的临时对象,至少理论上是这样的。但现实中,编译器经常会把一些对象

来源:https://www.tulaoshi.com/n/20160129/1486075.html

延伸阅读
  2004年4月20日最新版本的GCC编译器3.4.0发布了。目前,GCC可以用来编译C/C++、FORTRAN、JAVA、OBJC、ADA等语言的程序,可根据需要选择安装支持的语言。GCC 3.4.0比以前版本更好地支持了C++标准。本文以在Redhat Linux上安装GCC3.4.0为例,介绍了GCC的安装过程。 安装之前,系统中必须要有cc或者gcc等编译器,并且是可用的,或...
(********************* PL0 编译程序Turbo Pascal代码 *********************) program pl0(fa,fa1,fa2); (* PL0 compile with code generation *)   label 99;       (* Turbo Pascal do not support goto between different          ...
<C++实践系列C++中的异常(exception) 作者:张笑猛 提交者:eastvc 发布日期:2003-11-22 14:40:53 原文出处:http://objects.nease.net/ 1.简介   1.1常用的错误处理方式   1.2 不常用的处理方式   1.3 异常 2. 异常的语法   2.1 try   2.2 catch   2.3 throw   2.4 函数声明 3. 异常使用技巧...
介绍一个专门处理C++异常的类 作者:PJ Naughter 下载源代码和例子 简介: CExceptionLogger ,是一个可以免费使用的C++类,用它可以截获未处理异常,如:非法存取、栈溢出、被零除等,并可以将异常详细信息记录到日志文件。这个类源自于MSDN...
异常是程序运行中发生的错误,异常处理是程序设计的一部分。在c#中异常处理是通过Exception基类进行的,可以创建自己的异常类,但这个类必须是继承自Exception基类。 异常将导致不完善或者不需要的结果,因此在程序设计中需要处理异常。异常也可以是象"IndexOutOfBounds"这样的错误,这个错误表示程序试图访问数组中部存在...

经验教程

522

收藏

15
微博分享 QQ分享 QQ空间 手机页面 收藏网站 回到头部