我有三个专用函数,每个函数都包含一个
__try/_except
块,并且应分别抛出 EXCEPTION_ACCESS_VIOLATION
、EXCEPTION_INT_DIVIDE_BY_ZERO
和 EXCEPTION_FLT_DIVIDE_BY_ZERO
异常。还为每个__except
提供了一个过滤器。
EXCEPTION_ACCESS_VIOLATION
和 EXCEPTION_INT_DIVIDE_BY_ZERO
异常被抛出并成功捕获。
EXCEPTION_FLT_DIVIDE_BY_ZERO
不会抛出异常。为什么?
如何修复代码以捕获异常?
#include <Windows.h>
#include <iostream>
//------------------------------------------------------------------------------
int filter_access_violation(unsigned int code, EXCEPTION_POINTERS* xp)
{
if( code == EXCEPTION_ACCESS_VIOLATION )
{
std::cout << "OK: filter EXCEPTION_ACCESS_VIOLATION\n";
return EXCEPTION_EXECUTE_HANDLER;
}
std::cout << "Error: filter EXCEPTION_ACCESS_VIOLATION\n";
return EXCEPTION_CONTINUE_SEARCH;
}
void access_violation(int* pt)
{
std::cout << "\nTesting: EXCEPTION_ACCESS_VIOLATION\n";
__try
{
*pt = 1;
std::cout << "Error: EXCEPTION_ACCESS_VIOLATION not thrown\n";
}
__except (filter_access_violation(GetExceptionCode(), GetExceptionInformation()))
{
std::cout << "OK: __except EXCEPTION_ACCESS_VIOLATION\n";
}
}
//------------------------------------------------------------------------------
int filter_int_divide_by_zero(unsigned int code, EXCEPTION_POINTERS* xp)
{
if (code == EXCEPTION_INT_DIVIDE_BY_ZERO)
{
std::cout << "OK: filter EXCEPTION_INT_DIVIDE_BY_ZERO\n";
return EXCEPTION_EXECUTE_HANDLER;
}
std::cout << "Error: filter EXCEPTION_INT_DIVIDE_BY_ZERO\n";
return EXCEPTION_CONTINUE_SEARCH;
}
int int_divide_by_zero(int divisor)
{
std::cout << "\nTesting: EXCEPTION_INT_DIVIDE_BY_ZERO\n";
auto r = 1;
__try
{
r = 1 / divisor;
std::cout << "Error: EXCEPTION_INT_DIVIDE_BY_ZERO not thrown\n";
}
__except (filter_int_divide_by_zero(GetExceptionCode(), GetExceptionInformation()))
{
std::cout << "OK: __except EXCEPTION_INT_DIVIDE_BY_ZERO\n";
}
return r;
}
//------------------------------------------------------------------------------
int filter_float_divide_by_zero(unsigned int code, EXCEPTION_POINTERS* xp)
{
if (code == EXCEPTION_FLT_DIVIDE_BY_ZERO)
{
std::cout << "OK: filter EXCEPTION_FLT_DIVIDE_BY_ZERO\n";
return EXCEPTION_EXECUTE_HANDLER;
}
std::cout << "Error: filter EXCEPTION_FLT_DIVIDE_BY_ZERO\n";
return EXCEPTION_CONTINUE_SEARCH;
}
float float_divide_by_zero(float divisor)
{
std::cout << "\nTesting: EXCEPTION_FLT_DIVIDE_BY_ZERO\n";
auto r = 1.0f;
__try
{
r = 1.0f / divisor;
std::cout << "Error: EXCEPTION_FLT_DIVIDE_BY_ZERO not thrown\n";
}
__except( filter_float_divide_by_zero(GetExceptionCode(), GetExceptionInformation()) )
{
std::cout << "OK: __except EXCEPTION_FLT_DIVIDE_BY_ZERO\n";
}
return r;
}
int main()
{
access_violation(nullptr);
int_divide_by_zero(0);
float_divide_by_zero(0);
}
输出:
Testing: EXCEPTION_ACCESS_VIOLATION
OK: filter EXCEPTION_ACCESS_VIOLATION
OK: __except EXCEPTION_ACCESS_VIOLATION
Testing: EXCEPTION_INT_DIVIDE_BY_ZERO
OK: filter EXCEPTION_INT_DIVIDE_BY_ZERO
OK: __except EXCEPTION_INT_DIVIDE_BY_ZERO
Testing: EXCEPTION_FLT_DIVIDE_BY_ZERO
Error: EXCEPTION_FLT_DIVIDE_BY_ZERO not thrown
默认情况下,对于MSVC编译并在Windows上运行的C或C++程序, 浮点除以零不会引发操作系统异常(也不会引发操作系统异常) C++ 中的语言级异常)。
为了引起浮点除以零来引发操作系统 异常,调用 MSVC 特定的
_controlfp
功能如下:
_controlfp(0 /*new value*/, _EM_ZERODIVIDE /*mask of bits to change*/);
浮点行为由 32 位控制字控制, 可以使用
_controlfp
进行更改。 上面的调用设置了
_EM_ZERODIVIDE
标志为零。
异常掩码(EM)是控制字的一部分,它指定 哪些操作会引发异常。 当设置一个位时,该条件 是“屏蔽”的,意味着“不”引发异常。 清除该位 启用相应的异常。 注意这里“面具”一词的两种不同含义:一个是 浮点控制字的一部分表示“不”要引发什么, 而另一个是
_controlfp
API 的一个元素,说明 to 的内容 改变。
如果将上面这行代码作为main()
的第一行插入
问题中的代码,那么修改后的程序的输出是:
Testing: EXCEPTION_ACCESS_VIOLATION
OK: filter EXCEPTION_ACCESS_VIOLATION
OK: __except EXCEPTION_ACCESS_VIOLATION
Testing: EXCEPTION_INT_DIVIDE_BY_ZERO
OK: filter EXCEPTION_INT_DIVIDE_BY_ZERO
OK: __except EXCEPTION_INT_DIVIDE_BY_ZERO
Testing: EXCEPTION_FLT_DIVIDE_BY_ZERO
OK: filter EXCEPTION_FLT_DIVIDE_BY_ZERO
OK: __except EXCEPTION_FLT_DIVIDE_BY_ZERO <--- what we wanted
<float.h>
,尽管出现了包括
<windows.h>
足够了。
文档还说要添加:#pragma fenv_access (on)
但是,在我的测试中,我没有观察到省略它有任何区别 并包括它。
便携性考虑
在大多数平台上,相当于 GCC 的
_controlfp
feenableexcept
_controlfp
,如上所示。不幸的是,
C99标准浮点环境API
<fenv.h>