C++编译的C库如何处理运行时错误?

3
假设我有一个C库,我正在使用Visual Studio在Windows上将其链接到C++程序中。这个C库是黑盒子。如果此库发生致命错误(例如取消引用空值),程序/操作系统将如何处理此运行时错误?我知道在C++中有空引用异常,因此你可以使用try/catch处理此类错误,但由于这是一个C库,它不会发出throw,对吧?所以会发生什么?程序将终止,但如果不是C++异常,那么是通过哪种手段呢?

5
C++ 中的空引用异常?这不是标准要求吧?我相当确定我使用过的 C++ 实现只会产生段错误。 - ruakh
2
也许巧妙地利用atexit()可以有所帮助?不过我不确定。 - Michael Dorgan
也许我误解了,但我认为MSVC针对取消引用空指针发出异常。他们称之为“Null reference exception”,也许他们在这里并不是指C++异常?此外,当我启用调试异常(Visual Studio中的Debug菜单>> Exceptions...),当空指针取消引用时,我会得到一个中断。 - void.pointer
1
空引用异常 是一个.NET类。你是想说C++/CLI吗?那不是标准的C++。 - Wyzard
1
据我所知,这不是C++异常。它更类似于一个信号。 - R.. GitHub STOP HELPING ICE
感谢大家的澄清。仅仅通过日常调试很难判断像解引用空指针这样的操作是否会在 Microsoft 实现中引发异常。它们看起来像是异常,但实际上是信号。我需要更多地了解信号以及何时使用信号而不是异常。 - void.pointer
3个回答

6
您永远无法“处理”空指针的解引用。一旦这样做,您的程序就不再处于良好定义的状态,并且没有办法以确定性方式继续执行。唯一的行动是terminate(),如果您没有注册SIGSEGV处理程序,操作系统将会友好地为您执行。
单词“error”有几个可能会产生混淆的含义:一方面,无法执行其预期任务的函数可能会遇到“错误”,并且预期通过适当的返回值或抛出异常来表明此情况。这种行为可能更好地称为“失败”。正确的程序必须准备处理函数可能返回的所有可能方式。另一方面,存在导致错误或甚至形式错误的编程错误。正确的程序不能有任何编程错误。
例如,malloc()可能会失败(如果找不到足够的空间),它会通过返回空指针来表示,但如果程序在未进行检查的情况下对malloc()的结果进行了解引用,则该程序就有误。
您永远无法通过进一步的编程“捕获”或“处理”编程错误。相反,正确的程序必须正确预测并处理组件函数的所有失败条件,递归地,正确编写的函数必须始终以良好定义的方式返回并适当地发出失败信号。

但根据我的一般调试,解引用空指针会导致异常。因此,使用catch(...),你不能处理它吗? - void.pointer
1
@RobertDailey:不是的。异常是有意的,正确的编程的一部分。解引用空指针从来都不是正确的。我会在帖子中再添加另一个段落,请继续关注。 - Kerrek SB
1
@RobertDailey:不要混淆C++异常和CPU异常。它们是完全不同的东西。 - Alexey Frunze
非常好的回答,Kerrek。我熟悉关于异常或者你所说的“失败”的一般原则和设计。但是我不熟悉“CPU异常”。编程错误处理的实现才是这里真正需要解决的问题。虽然你的回答包含了一些非常好的信息,但它并没有完全回答我最主要的问题。我仍然点了赞 :) - void.pointer

4
在 Native 代码中引用 null 指针会导致 Windows 上的 "access violation" 错误,或 Unix/Linux 上的 "segmentation fault" 错误。这是同一个错误的不同名称。CPU 检测到错误并调用操作系统中的处理程序终止进程。
在基于 VM 的语言(例如 .NET 或 Java)中引用 null 引用将抛出异常,您可以捕获该异常。这是由于 VM 位于程序和 CPU 之间,因此它可以在尝试实际解引用之前检测到 null。
C 库是本地代码,因此会收到访问冲突的错误。您将从真正的 C++ 程序中获得相同的结果,该程序也编译为本地代码。但是,如果您使用的是 Managed C++ 或 C++/CLI,则这些是编译为 CIL 并在 .NET 运行时上运行的变体,因此在这种情况下会出现 NullReferenceException。

“Invalid page fault”这个错误信息可以追溯到上个世纪,而在Windows NT分支中则被称为“访问冲突”。 - Hans Passant
@HansPassant,糟糕。 :-) 我实际上并不经常使用Windows,在罕见的情况下,当某些东西崩溃时,我只会看到“遇到问题需要关闭”对话框,所以我没有注意到术语已更改。已修复。 - Wyzard
这太棒了,正是我所需要的!这解释了实现如何处理这样的错误(比如取消引用空指针)的细节,这正是我在我的原始帖子中询问的内容。 - void.pointer

2
支持使用C++的catch关键字捕获“异步”或“结构化”异常(例如无效内存访问)是MSVC对C++语言的扩展。只要使用正确的编译器选项/EHa,即使这些异常来自C函数,也可以捕获这些异常:http://msdn.microsoft.com/en-us/library/1deeycx5%28v=VS.100%29.aspx 你还可以选择使用Microsoft的“结构化异常”处理语言扩展(也适用于C),该扩展使用__try__except关键字:http://msdn.microsoft.com/en-us/library/swezty51.aspx

你说过异常可能来自C函数,但是为什么呢?C函数应该与C保持向后兼容,而C并不支持异常。能否解释一下? - void.pointer
该异常源自Windows操作系统和运行时库。它不是C++异常,就像Michael所说的那样,它是“Windows结构化异常”。这些异常可以通过__try和__except块捕获,这是C和C++语言的Microsoft扩展。进一步的Microsoft扩展允许C++的catch也能够捕获这些结构化异常。 - James Caccese

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接