在使用Windows程序的时候,相信没有用户喜欢看到程序突然崩溃的情形吧!为了避免程序崩溃,程序员在编写程序的时候最好在容易出错的地方使用异常处理机制来保证友好的用户体验。特别是编写C/C++代码的时候,崩溃是经常的事情!
今天一位同事给我说,编写C/C++代码崩溃的原因主要是因为内存操作违规。如果每次操作一块内存或指针之前都检查内存或指针是否有效,那么可以降低程序崩溃的次数。但是这会让程序员很厌烦的,哈哈。所以在适当的地方加上异常处理,即使崩溃也会让程序员更好的改善程序。当然,程序效率必然降低!
幸好C++规范中有异常处理机制: try catch
但是在Visual Studio中直接使用try catch是不能产生异常的,必须手动抛出异常throw,见如下代码:
void TryCatchfourth()
{
char* Test = NULL;
try
{
Test = new char[2];
FreeArray(Test);
throw 0; // 这里我是随便抛出异常以测试
// 若没有手动throw,后面即使产生异常catch里面的代码还是不会被执行
puts("No "); // 接下来这两句不会被执行
*(Test + 4096 ) = '\0';
}
catch (...)
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("4 \n");
}
puts("Fourth");
}
又下面这段代码,如果将vs编译器的选项修改:打开项目属性→配置属性→C/C++→代码生成→启用C++异常→是,但有SEH异常(/EHa) (这里编译器默认为"是(/EHsc)")。
void TryCatchThree()
{
char* Test = NULL;
try
{
Test = new char[2];
FreeArray(Test);
*(Test + 4096 ) = '\0';
}
catch (...)
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("4 \n");
}
puts("Three");
}
修改编译器选项之后执行TryCatchThree()就正常工作。请注意TryCatchThree()和TryCatchfourth()所采用的不同编译选项。
最后参考一个Windows的结构化异常处理代码:
void TryCatchSecond()
{
char* Test = NULL;
__try
{
__try
{
Test = new char[2];
FreeArray(Test);
*(Test + 4096 ) = '\0';
// 此处数字一定要大,否则不能产生异常。
// 因为Windows平台下,new N个字节,系统分配的一定比N大。
// 4096大小刚好是一个页面大小,如果N>4096,那就自己扩大对应数字以产生异常
}
__finally
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("2 "); // this is printed second
}
}
__except ( FilterFunction(GetExceptionCode()) )
{
printf("3 \n"); // this is printed last
}
puts("Second");
}
不管编译器选项是否按照上述要求被修改,TryCatchSecond()均能正常工作。同事说,这是Windows结构化异常,和C++中的异常有点不一样,我现在也不太懂,以后查到资料后在添加到博客中来。TryCatchSecond()代码是参考MSDN中的一个例子,如下:
DWORD FilterFunction(int i = 1)
{
printf("%d ", i); // printed first
return EXCEPTION_EXECUTE_HANDLER;
}
void TryCatchFirst()
{
char* Test = NULL;
__try
{
__try
{
// 这个API是手动设置异常代码(这么称呼有点别扭)
RaiseException(1, // exception code
0, // continuable exception
0, NULL); // no arguments
}
__finally
{
printf("2 "); // this is printed second
}
}
__except ( FilterFunction() )
{
printf("3 \n"); // this is printed last
}
puts("One");
}
下面是完整代码:(本文中代码均在vs2008中编写和测试,建议不要在vc6下测试,vc6对C++规范支持很不好)
#include <windows.h>
// 释放数组内存
#define FreeArray(pArray) { \
delete[] pArray; \
pArray = NULL; \
}
DWORD FilterFunction(int i = 1)
{
printf("%d ", i); // printed first
return EXCEPTION_EXECUTE_HANDLER;
}
void TryCatchFirst()
{
char* Test = NULL;
__try
{
__try
{
// 这个API是手动设置异常代码(这么称呼有点别扭)
RaiseException(1, // exception code
0, // continuable exception
0, NULL); // no arguments
}
__finally
{
printf("2 "); // this is printed second
}
}
__except ( FilterFunction() )
{
printf("3 \n"); // this is printed last
}
puts("One");
}
void TryCatchSecond()
{
char* Test = NULL;
__try
{
__try
{
Test = new char[2];
FreeArray(Test);
*(Test + 4096 ) = '\0';
// 此处数字一定要大,否则不能产生异常。
// 因为Windows平台下,new N个字节,系统分配的一定比N大。
// 4096大小刚好是一个页面大小,如果N>4096,那就自己扩大对应数字以产生异常
}
__finally
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("2 "); // this is printed second
}
}
__except ( FilterFunction(GetExceptionCode()) )
{
printf("3 \n"); // this is printed last
}
puts("Second");
}
void TryCatchThree()
{
char* Test = NULL;
try
{
Test = new char[2];
FreeArray(Test);
*(Test + 4096 ) = '\0';
}
catch (...)
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("4 \n");
}
puts("Three");
}
void TryCatchfourth()
{
char* Test = NULL;
try
{
Test = new char[2];
FreeArray(Test);
throw 0; // 这里我是随便抛出异常以测试
// 若没有手动throw,后面即使产生异常catch里面的代码还是不会被执行
puts("No "); // 接下来这两句不会被执行
*(Test + 4096 ) = '\0';
}
catch (...)
{
if (NULL != Test)
{
FreeArray(Test);
}
printf("4 \n");
}
puts("Fourth");
}
VOID main(VOID)
{
// TryCatchFirst();
// TryCatchSecond();
// TryCatchThree();
// TryCatchfourth();
}
【参考资料 感谢作者】
1、我的同事
快捷操作:
坚其志,苦其心,劳其力,事无大小,必有所成。
@如有侵权,请作者本人尽快与我(chrayo#163.com)联系,我将及时删除侵权内容。