EXCEPTION_FLT_DIVIDE_BY_ZERO未抛出原因及代码修复方案
问题描述
编写三个独立函数,每个函数包含__try/__except块,预期分别抛出EXCEPTION_ACCESS_VIOLATION、EXCEPTION_INT_DIVIDE_BY_ZERO、EXCEPTION_FLT_DIVIDE_BY_ZERO异常,且每个__except配备对应过滤器。目前前两个异常可成功抛出并捕获,但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
原因分析
Windows平台下,浮点运算异常默认被CPU/FPU的控制字屏蔽,不会触发结构化异常(__try/__except捕获的是Windows结构化异常)。而整数除零、访问违例这类异常属于硬件直接触发的结构化异常,不受此屏蔽影响,因此能被正常捕获。
当执行浮点除零操作时,默认行为是生成无穷大值(INF)而非抛出结构化异常,所以__except块无法捕获到EXCEPTION_FLT_DIVIDE_BY_ZERO。
解决方法
需要通过修改浮点控制字,启用浮点除零异常的抛出,使其转化为可被__try/__except捕获的结构化异常。可以使用C标准库的_controlfp_s函数来修改控制字,解除_EM_ZERODIVIDE异常的屏蔽。
修改后的代码
在浮点除零测试函数中添加控制字设置:
#include <Windows.h> #include <iostream> #include <float.h> // 引入_controlfp_s的头文件 //------------------------------------------------------------------------------ // 以下两个过滤器和函数代码与原示例一致,省略重复内容 //------------------------------------------------------------------------------ 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; // 保存原有浮点控制字,修改为启用除零异常 unsigned int old_control_word; _controlfp_s(&old_control_word, 0, 0); // 获取当前控制字 unsigned int new_control_word = old_control_word & ~_EM_ZERODIVIDE; // 解除除零异常屏蔽 _controlfp_s(nullptr, new_control_word, _MCW_EM); // 设置新控制字 __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"; } // 恢复原有浮点控制字,避免影响后续代码 _controlfp_s(nullptr, old_control_word, _MCW_EM); return r; } // 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
内容的提问来源于stack exchange,提问作者Alex Net
相关产品推荐
相关产品推荐

