本文已参与「新人创作礼」活动,一起开启掘金创作之路。
@[toc]
前言
结构化异常处理,SEH(Structured Exception Handling)
SEH的好处:
- 就是当你编写程序时,只需要关注程序要完成的任务。如果在运行时发生什么错误,系统会发现并将发生的问题通知你。
- 使用 S E H所造成的负担主要由编译程序来承担,而不是由操作系统承担。当异常块(exception block)出现时,编译程序要生成特殊的代码。编译程序必须产生一些表( t a b l e)来支持处理S E H的数据结构。编译程序还必须提供回调( c a l l b a c k)函数,操作系统可以调用这些函数,保证异常块被处理。编译程序还要负责准备栈结构和其他内部信息,供操作系统使用 和参考。
SEH 包含两个主要功能:
- 结束处理
- 异常处理
SEH和c++异常处理区别: 不要将结构化异常处理同 C + +的异常处理相混淆。C + +异常处理是一种不同形式的异常处理,其形式是使用C + +关键字catch和throw。微软的Visual C++也支持C + +的异常处理,并且在内部实现上利用了已经引入到编译程序和 Windows操作系统的结构化异常处理的功能。
一个结束处理程序能够确保去调用和执行一个代码块(结束处理程序,termination handler), 而不管另外一段代码(保护体, guarded body)是如何退出的。结束处理程序的文法结构(使用微软的Visual C++编译程序)如下:
__try
{ //Guard body
...
}
__finally
{ // Terminal Handler
...
}
__try和__finally关键字用来标出结束处理程序两段代码的轮廓。在上面的代码段中,操作系统和编译程序共同来确保结束处理程序中的__finally 代码块能够被执行,不管保护体(try块)是如何退出的。不论你在保护体中使用 return,还是goto,或者是longjump,结束处理程序(finally块)都将被调用。
总之,c++异常处理顺序是: 1.try 2.finally 3. try里的return或者finally之后的代码
C++异常处理的情况
情况1 释放信标情况
代码要等待信标( semaphore),改变保护数据的内容,保存局部变量dwTemp的新值,释放信标,将新值返回给调用程序。
DwORD Funcenstein1( ) {
DWORD dwTemp;
//1. Do any processing here.
__try {
//2. Request permission to access
//protected data. and then use it.waitForSing1e0bject(g_hSem,INFINITE);g_dwProtectedData = 5;
dwTemp = g_dwProtectedData ;
}
__fina1ly {
//3.A1low others to use protected data.Re1easeSemaphore( g_hSem,1,NULL);
}
//4. Continue processing.
return(dwTemp);
}
最后的释放信标放在finally 是一定会执行的。
情况2 释放信标 try中加入return
DwORD dwTemp;
// 1. Do any processing here.:
__try {
// 2. Request permission to access
// protected data, and then 'use it.
waitForSing1e0bject(g_hSem,INFINITE);
g_dwProtectedData = 5;
dwTemp = g_dwProtectedData ;
//Return the new value.
return(dwTemp) ;
}
__fina1ly {
//3.Al1ow others to use protected
data.Re1easeSemaphore( g_hSem,1,NULL);
}
// Continue processing--this code
// wi11 never execute in this version.dwTemp = 9;
return(dwTemp );
}
结果是,返回5。因为当提前return时,finally会先执行,然后再执行try内的return。
当return语句试图退出try块时,编译程序要确保finally块中的代码首先被执行。要保证finally块中的代码在try块中的return语句退出之前执行。Funcens tein2中,将对ReleaseSemaphore的调用放在结束处理程序块中,保证信标总会被释放。这样就不会造成一个线程一直占有信标,否则将意味着所有其他等待信标的线程永远不会被分配CP U时间。
编译程序是如何保证在try块可以退出之前执行finally块的? 查看总结4
情况3 释放信标 在try内使用Goto
DwORD Funcenstein3( ) {
DwORD dwTemp;
//1. Do any processing here.
__try {
//2. Request permission to access
//protected data, and then use it.
WaitForSingle0bject( g_hSem,INFINITE);g_dwProtectedData = 5;
dwTemp = gdwProtectedData;
//Try to jump over the fina11y b1ock.
goto ReturnValue;
}
__fina11y {
//3.Al1ow others to use protected data.
Re1easeSemaphore( g_hSem,1.NULL);
}
dwTemp = 9;
//4. Continue processing.Returnvalue:
return(dwTemp);
}
结果是:goto不会执行。会先try到goto时先finally在到retunrn(dwTemp)。
当编译程序看到try块中的goto语句,它首先生成一个局部展开来执行 finally 块中的内容。这一次,在 finally 块中的代码执行之后,在 ReturnValue 标号之后的代码将执行,因为在try块和finally块中都没有返回发生。这里的代码使函数返回 5。而且,由于中断了从try块到finally块的自然流程,可能要蒙受很大的性能损失(取决于运行程序的CPU)。
情况4 释放信标 try调用函数抛异常
try块中的Funcinator函数调用包含一个错误,会引起一个无效内存访问。
DMORD Funcfurter1(){
DWORD dwTemp;
//1. Do any processing here.
__try{
//2. Request permission to access
//protected data. and then use it.
WaitForSing1e0bject( g_hSem,INFINITE);
dwTemp = Funcinator( g_dwProtectedData) ;
}
__final1y {
//3.A11ow others to use protected data.
ReleaseSemaphore( g .hSem,1,NULL);
)
//4. Continue processing.
return( dwTemp);
)
结果是,正常释放信标,进程不会提前结束,导致信标没释放。不然就可能引起其他进程等待。
如果结束处理程序足够强,能够捕捉由于无效内存访问而结束的进程,我们就可以相信它也能够捕捉setjump和longjump的结合,还有那些简单语句如break和continue。
情况5 循环中使用try-finally
如下代码执行结果如何?
DWORD FuncaDoodleDoo( ) {
DWORD dwTemp = 0;
while (dwTemp < 10){
__try {
if ( dwTemp == 2)
continue;
if ( dwTemp == 3 )
break;
}
__fina11y {
dwTemp+t;
}
dwTemp++;
}
dwTemp += 10;
return(dwTemp);
}
总结
1.SEH(Structured Exception Handling)和SJLJ(SetJump LongJump)区别对比
SEH:
2.SEH和c++异常处理区别?
SEH和c++异常处理区别: 不要将结构化异常处理同 C + +的异常处理相混淆。C + +异常处理是一种不同形式的异常处理,其形式是使用C + +关键字catch和throw。微软的Visual C++也支持C + +的异常处理,并且在内部实现上利用了已经引入到编译程序和 Windows操作系统的结构化异常处理的功能。
3.C++异常处理的特点?
- 通过使用结束处理程序,可以避免 return语句的过早执行。 例如:try内return时 finally可以避免提前返回而没有释放。
注意除了以下几种情况不能处理:
- 当调用ExitThread或ExitProcess时,将立即结束线程或进程,而不会执行 finally块中的任何代码。
- 另外,如果由于某个程序调用 TerminalThread或 TerminalProcess,线程或进程将死掉,finally 块中的代码也不执行。
- 某些 C运行期函数(例如abort)要调用ExitProcess,也使finally块中的代码不能执行。
4. 编译程序是如何保证在try块可以退出之前执行finally块的?
当编译程序检查源代码时,它看到在try块中有return语句。这样,编译程序就生成代码将返回值 保存在一个编译程序建立的临时变量中。编译程序然后再生成代码来执行finally块中包含的指令,这称为局部展开。更特殊的情况是,由于try块中存在过早退出的代码,从而产生局部展开,导致系统执行finall y块中的内容。在finally块中的指令执行之后,编译程序临时变量的值被取出并从函数中返回。
故在编写代码时,就应该避免引起结束处理程序的t r y块中的过早退出,因为程序的性能会受到影响。