【发布时间】:2020-05-21 06:27:41
【问题描述】:
我正在开发 MFC 应用程序。我们最近将应用程序从使用 MSVC 2010 编译器(v100 工具集)迁移到使用 MSVC 2019 编译器(v142 工具集)。升级到 MSVC 2019 后,我们发现应用程序的行为有所不同。
在使用MSVC 2010编译器(v100 Toolset)编译的Application中,当Application的主窗口关闭时,Application通过调用Main frame window类的OnClose()函数启动退出进程。在销毁序列期间,我们在其中一个类的析构函数中记录了代码,该类实际上最终解除了对悬空指针的引用(此指针指向日志记录类的实例,并且导致访问冲突的行正在尝试调用此函数之前已经销毁的日志记录类实例)。发生此访问冲突时,我可以在 Visual Studio 2010 输出窗口中看到消息“在 :0xC0000005 中的第一次机会异常:位置的访问冲突”。我们在这个特定的调用层次结构中没有任何 catch 块来捕获这个访问冲突异常,因此我们希望应用程序终止。按下F10后(看能不能移动到下一行),此时显示的调用栈如下图:
exe_name!'class_name::function_name'::'1'::catch$0() Line line_no
Msvcr100.dll!_CallSettingFrame() 第 44 行
Msvcr100.dll!_CxxCallCatchBlock(_EXCEPTION_RECORD * pExcept) 第 1337 行
当我调试并按 F10 时,我能够继续越过导致访问冲突的行。似乎运行时正在抑制异常(或以某种方式捕获它),因为我能够继续越过导致访问冲突的行。任何人都可以解释为什么即使在出现未处理的异常之后应用程序仍在继续。
我们在 VC 10 和 VC 19 的 exe 的项目属性中为“C/C++ -> 代码生成 -> 启用 C++ 异常”选择了编译器选项“是 SEH 异常 (/EHa)”。
在使用 MSVC 2019 编译器(v142 工具集)编译的应用程序中,完全相同的代码行会导致运行时检测到纯虚函数调用并且应用程序崩溃。我想了解的正是这种行为差异(在 MSVC 2010 编译的应用程序和 MSVC 2019 编译的应用程序之间)。
我知道取消引用悬空指针会导致未定义的行为。但是,我无法理解在 VC 2010 编译的应用程序中如何调试(或继续)导致访问冲突异常(尚未处理)的行。
【问题讨论】:
-
欢迎来到 Stack Overflow!
-
您甚至可能会在同一个编译器上看到不同的行为,但优化级别不同。正如您所说,取消引用空指针是未定义的行为。这意味着编译器在生成机器代码时可以假设这永远不会发生。不同级别的优化可以(并且经常这样做)生成导致 UB 表现不同的机器代码。见鬼,即使在调试器的监督下运行程序也会导致 UB 表现不同。