代码规范之旅一 __try __finally __leave

本文详细解析了C++中结构化异常处理(SEH)机制中的try-finally语句用法,并强调了在try块和finally块中使用return等语句的影响。通过示例代码展示了如何规范地使用try-finally来确保资源正确释放。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/*
结论* try块和finally块中不允许出现return break continue goto等改变执行流程的语句

1  	如果try块中有return语句 finally不会因为这条语句失去执行机会
	但是会让编译器生成额外的代码 因此要跳出try块应该使用__leave
	
2  	如果try块和finally块中同时有return语句
	编译器会做局部展开(分配栈空间) 保存try块中的返回值 然后执行finally中的语句
	当执行到finally块的return语句时 会将finally的返回值写入到局部展开内存中 
	覆盖try块中的返回值 因此finally中不应该使用return语句
*/

/***********************规范的try finally用法***********************/
HANDLE hFile = INVALID_HANDLE_VALUE;
PVOID pvBuf = NULL;
BOOL bFunctionOk = FALSE;

__try
{
	//---------------------打开文件---------------------
	hFile = CreateFile(TEXT("some data.DAT"), GENERIC_READ,
		FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	if (hFile == INVALID_HANDLE_VALUE)
		__leave;
	
	//---------------------分配内存---------------------
	pvBuf = VirtualAlloc(NULL, 1024, MEM_COMMIT, PAGE_READWRITE);
	if (pvBuf == NULL)
		__leave;
	
	//---------------------将文件内容读入内存---------------------
	DWORD dwNumBytesRead;
	BOOL bOk = ReadFile(hFile, pvBuf, 1024, &dwNumBytesRead, NULL);
	if (!bOK || (dwNumBytesRead == 0))
		__leave;
		
	bFunctionOk = TRUE;
}
__finally
{
	if (pvBuf != NULL)
		VirtualFree(pvBuf, MEM_RELEASE | MEM_DECOMMIT);
	if (hFIle != INVALID_HANDLE_VALUE)
		CloseHandel(hFile);
}

return bFunctionOk;


扩展知识点——局部展开和全局展开

局部展开就是指某个函数中的__try块中的代码因为执行了return,break,goto,continue等指令使得指令流要提前流出__try块或者因为指令流到了__try块底部要自然流出,这时进行局部展开,说白了就是执行__finally块中的代码,以保证__finally语义的正确性。这里注意局部展开是代码的自然执行过程,而不是由异常等引起的。

全局展开则是由异常引起的,所以这里先大致说下SEH对异常的处理过程,当某个__try块中的代码触发了异常时(也可能是__try块中调用的函数中引发异常),操作系统会从最靠近引发异常代码的地方开始从下层往上层查找__except块(这里的层是指__try块的嵌套层),对于找到的每一个__except块,会先计算它的异常过滤器,如果过滤器返回EXCEPTION_CONTINUE_SEARCH,则说明此__except块不处理此类异常,需要继续往上层查找,如果某过滤器返回EXCEPTION_EXECUTE_HANDLER则说明此__except块可以处理此类异常,即找到了异常的处理代码,此时停止查找,但是在执行该__except块中的异常处理代码之前,要先进行全局展开。全局展开的过程与查找__except块的过程类似,只不过这次是查找从底层向上查找__finally块,查找过程中遇到的每一个__finally块中的代码都被执行,直到查找到前面说的处理异常的__except块那一层停止,这时全局展开完成,然后执行__except块中的异常处理代码,执行完异常处理代码之后,指令流从__except块后的第一条指令开始。从这里也可以看出全局展开也是为了保证__finally语义的正确性,因为指令流从引发异常代码转到到__except异常处理代码时也导致了指令流从__try块嵌套层中所有与__finally对应的__try块中流出,由前面的__finally语义说明可知,此时必须要执行全局展开过程以包成__finally语义的正确性。

这里另外做一个特别说明,前面提到了两种过滤器返回值(EXCEPTION_CONTINUE_SEARCH和EXCEPTION_EXECUTE_HANDLER),只有过滤器返回EXCEPTION_EXECUTE_HANDLER才可能会引发全局展开,在windows SEH中还有一种过滤器返回值是EXCEPTION_CONTINUE_EXECUTION,它的意思是指令流重新返回引发异常的代码,重新执行一次。这个返回值也不会引起全局展开,因为既然指令流返回了引发异常代码处,表明没有流出__try块,所以不需要展开。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值