C++编译器怎么实现异常处理3

原创 2002年11月17日 11:51:00

C++和异常

再回头来说我们在第一节里说到的 EXCEPTION_REGISTRATION结构,这个结构是用来注册操作系统的异常回调函数的,当异常发生时,该函数将被调用。

VC++扩展了异常回调函数得语法,增加了两个新的参数:

struct EXCEPTION_REGISTRATION
{
   EXCEPTION_REGISTRATION *prev;
   DWORD handler;
   int   id;
   DWORD ebp;
};

VC ++里,除了一些例外,在每个函数的头部生成EXCEPTION_REGISTRATION结构的局部变量(作者注:编译器可能在一个函数里根本不生成任何异常相关的代码,如果函数里没有try或者所有的局部对象都没有可供调用的析构函数(译注:当异常产生时,要把堆栈顶到catch到异常那点之间的局部变量都释放掉,这是异常处理一个重要责任。如果这些局部变量都不需要去特别释放,比如int变量或简单结构变量,只要把堆栈指针指过来就可以了,异常处理在这一段里就是没有必要的,因此编译器识别了这样的情况,作个一定的优化))。上面结构的ebp和前面说的堆栈帧里的ebp指针是重叠在一起的(译注:请参看C++编译器怎么实现异常处理2),函数在开始时把这个结构创建在它的堆栈上,同时把这个结构注册到操作系统(译注:就是把FS:[0]的位置改成这个结构的指针);在函数的结尾恢复调用者在系统注册的EXCEPTION_REGISTRATION结构(译注:就是把FS:[0]改成结构里的prev),我将在下一节讨论EXCEPTION_REGISTRATION结构里的id域的重要性。

当VC++编译一个函数,它生成函数的两套数据:

a) 异常的回调函数

b) 一个包含重要信息的数据结构,这些重要信息比如,各个catch块,它们的地址,关心的异常的种类等等,我将在下一节更多的谈论它

 4 显示了如果考虑异常处理,程序运行时堆栈的情况。Widget的异常回调是在异常链的头部(异常链的头部就是FS:[0]指向的内容),FS:[0]的内容就是在Widget头部定义的EXCEPTION_REGISTRATION结构的指针。当异常产生时,异常处理把Widget的函数信息结构的地址(就是EXCEPTION_REGISTRATION的指针)传递给__CxxFrameHandler函数,__CxxFrameHandler函数检查这个数据结构,察看函数里是否有catch块对当前的异常感兴趣,如果没有找到,函数返回ExceptionContinueSearch给操作系统,操作系统获得异常处理链里的下一个节点,再调用这个节点的异常处理函数(这个节点应该是在本函数的调用者定义的)

figure4.gif

这个动作一直持续到异常处理找到一个对这个异常感兴趣的catch块,找到了匹配的catch块以后,程序不再返回到操作系统。但是在它调用catch块前(在函数信息结构里有这个异常回调函数的指针,看图4),异常处理必须执行堆栈释放:清除这个函数之前的所有函数的堆栈帧。清除堆栈帧的动作有点复杂,异常处理必须找到所有在异常产生时还没有结束的函数,找到每个函数所有的局部变量,调用每个变量的析构函数。我将在以后的章节里更详细的讨论这点。

异常处理是这样一个任务,当异常处理时清除异常处理所在的函数帧。这个操作是从FS:[0]指向的位置,也就是异常处理链头开始的, 然后沿着链一个节点一个节点的通知它所在的堆栈将要被回收,收到通知的节点,调用所在帧的所有局部对象的析构然后返回,这个过程一直延续到抛出的异常被捕获到。

因为catch块也是某个函数的一部分,所以它也用了所在函数的函数帧来保存数据。因此异常处理的一个catch块进入的时候会激活所在函数的那一帧(译注:就是会把ebp改到那个函数去,而esp是不动的,简单的说,在catch函数块里执行的时候,ebp是指向所在函数的帧顶,而esp可能在非常远的堆栈顶端)。同时,每个能能捕获异常的catch块(就是异常种类和catch块要捕获的异常种类一致)实际上只有一个参数,就是这个异常对象的拷贝或者是异常对象的引用拷贝。catch块知道怎么从函数信息结构中拷贝这个异常对象,编译器已经产生了足够多的信息(译注:就是根据传入的id,来判断怎么复制异常)。

当拷贝了异常对象同时激活了函数帧以后,异常处理就开始调用catch块,catch块的返回告诉异常处理在try-catch后面跟着执行(译注:当然也可以在catch块里接着throw)。注意,在这一时刻,即使堆栈恢复已经存在,而且函数帧都已经清理,但是这些地址仍然在堆栈上存在(译注:就是说在进入catch块函数时,仍然是用程序的堆栈,不过多压了好多东西进去,而在栈顶之前的一些东西都已经被清理了,但是它们仍然在堆栈上占据着空间,这个时间是检查错误来源的好时机,这也是我研究异常处理详细机制的目的),这是因为异常处理仍然像其他函数一样的执行,它也使用程序堆栈来存放它的局部变量,cantch块存放局部变量的帧在异常产生时调用的最后一个函数的堆栈帧的上边(地址更低),当catch块返回时,需要删除这个异常对象的拷贝(译注:如果异常对象是在栈里,自动就删除了,如果是在堆了,需要用delete或者free明确删除)。在catch块的结尾,异常处理回收了包括自己的帧在内的所有已经清理的堆栈帧,实际上就是把esp指向当前函数帧的结尾(译注:前面说过在catch块里,ebp是catch所在函数的ebp比如是0x12ff58,而esp在很远的地方比如0x12f9bc,而实际上,esp到ebp之间的大部分东西都已经被删除了,现在把esp改回来,回到函数正常运行的状态,比如0x12ff80,0x12ff58-0x12ff80实际上是拥有catch块的那个函数的堆栈帧的范围),完成这点以后,跳到try-catch块的下一条语句继续执行。但是catch块是怎么知道函数堆栈帧的尾在哪里的?这本来是没有办法知道的,这就是为什么编译器要在函数的堆栈帧的开头保存一个esp的原因。参看图4,堆栈帧指针EBP减去16就是esp所在的位置。

 catch块自己可能会产生一个新的异常或者把当前异常重新抛出。异常不得不监视这种情况然后采取适当的操作。如果异常处理抛出一个新的异常,前一个异常就被删除。如果catch块决定再次抛出捕获的异常,前一个异常就被往后传递。

有一个需要注意的地方:因为每个线程都有自己的堆栈,所以都有它自己的一套异常处理链(由FS:[0]处指向的EXCEPTION_REGISTRATION 结构开始)

C++编译器怎么实现异常处理

对于VC++实现异常处理的深入探讨   导论   相比较其他传统的语言,C++的一个变革的特征是支持异常处理。相对于传统语言的不清楚容易错误的错误处理机制,C++的异常处理是一个非常好的替代。在正常的...
  • iiprogram
  • iiprogram
  • 2008年03月18日 21:36
  • 627

C++编译器怎么实现异常处理2

看了C++编译器怎么实现异常处理1    sdssly(翻译)http://www.csdn.net/Develop/article/15%5C15051.shtm没有下文,于是自己去看原文,也翻译了...
  • ancienttale
  • ancienttale
  • 2002年11月15日 14:01
  • 943

C++编译器怎么实现异常处理4

C++和异常2图 5 显示了函数信息(funinfo)结构的内容。请注意结构使用的名字可能和VC++编译器使用的实际名字不一样,而且我在图中只显示了有关的成员,结构中的unwind table成员我将...
  • ancienttale
  • ancienttale
  • 2002年11月21日 10:06
  • 1031

C++编译器怎么实现异常处理1

C++编译器怎么实现异常处理 对于VC++实现异常处理的深入探讨 导论 相比较其他传统的语言,C++的一个变革的特征是支持异常处理。相对于传统语言的不清楚容易错误的错误处理机制,C++的异常处理是一个...
  • sdssly
  • sdssly
  • 2002年09月17日 09:28
  • 1017

自制编译器---c++实现词法分析器

词法单元词法解析器在编译器中的作用,是将输入流解析为一种能够被语法解析器使用和管理的格式。他将输入文本分割,打标签,也就是用一些数值来指代一系列相应的字符串。 例如:int a,b,c; a=34;...
  • taoyanqi8932
  • taoyanqi8932
  • 2016年06月25日 21:45
  • 2092

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

C++编译器如何实现异常处理 原著:Vishal Kochhar 翻译:局部变量 原文出处:How a C++ compiler implements exception handlin...
  • keroro520
  • keroro520
  • 2013年12月21日 11:52
  • 529

晒晒C++:虚函数的真相(VC编译器如何实现“virtual ”规则)

可到 CSDN 下载中心下载全文 http://download.csdn.net/detail/Dreamcode/201005 ( 1 )virtual 虚函数 先看一段简单代码:Code Seg...
  • Dreamcode
  • Dreamcode
  • 2011年12月14日 22:39
  • 14178

c++类的编译器实现方式描述

貌似有些同学还不太明白这个,我试着用c代码描述c++类相关的一些实现方式。   设类abc,bc继承于a,都有个虚函数f(),析构函数为虚 c++代码 //---------A  struc...
  • Kevin_qing
  • Kevin_qing
  • 2011年08月24日 15:04
  • 1097

深入理解C++中的异常处理机制

深入理解C++中的异常处理机制异常处理增强错误恢复能力是提高代码健壮性的最有力的途径之一,C语言中采用的错误处理方法被认为是紧耦合的,函数的使用者必须在非常靠近函数调用的地方编写错误处理代码,这样会使...
  • zhangyifei216
  • zhangyifei216
  • 2015年12月26日 21:29
  • 3070

C++入门(1):程序、编译器和操作系统

程序、编译器和操作系统参考书目《C++ primer》《编译原理基础》 在开始任意一门语言的学习之前,很多人习惯于使用一个“软件”(如visual studio 2010),这个软件的功能是你可以...
  • huiyuanliyan
  • huiyuanliyan
  • 2016年06月27日 11:41
  • 2132
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C++编译器怎么实现异常处理3
举报原因:
原因补充:

(最多只允许输入30个字)