C++ 调试编译的可执行文件:为什么不在无效写入NULL时优雅地中止?

C++ 调试编译的可执行文件:为什么不在无效写入NULL时优雅地中止?,c++,error-handling,debugging,segmentation-fault,C++,Error Handling,Debugging,Segmentation Fault,关于C/C++我不了解的是: 是的,每个人都使用它来获得极快的可执行文件,所以他们在编译时启用了优化 但是对于打开调试信息的编译,我们不关心速度。那么,为什么不在编译模式中包含更多信息,例如,在故障发生之前检测一些故障?实际上,在每次访问指针ptr之前插入一个assert(ptr!=NULL)。为什么编译器不能这样做?再一次,默认情况下应该关闭,但我认为应该有这样的可能性 编辑:一些人说,我建议的检测没有意义,或者没有做任何分段错误报告不会做的事情。但我想到的只是一个更优雅、信息更丰富的中止,它

关于C/C++我不了解的是:

是的,每个人都使用它来获得极快的可执行文件,所以他们在编译时启用了优化

但是对于打开调试信息的编译,我们不关心速度。那么,为什么不在编译模式中包含更多信息,例如,在故障发生之前检测一些故障?实际上,在每次访问指针
ptr
之前插入一个
assert(ptr!=NULL)
。为什么编译器不能这样做?再一次,默认情况下应该关闭,但我认为应该有这样的可能性


编辑:一些人说,我建议的检测没有意义,或者没有做任何
分段错误报告
不会做的事情。但我想到的只是一个更优雅、信息更丰富的中止,它会打印出出错代码的文件名和行号,就像
assert()
一样

在这种情况下,程序应该做什么?如果它通知用户一个bug,那么segfault就是这么做的

如果它应该继续运行并避免错误,它怎么知道该做什么

更不用说,如果它以某种方式神奇地知道如何正确继续,那么您的发布版本中就有一个bug(调试版本旨在帮助您识别和修复bug,而不是隐藏它们)


作为对问题补充信息的回应(我想我误解了你的意图):

我想到的只是一个更优雅、信息更丰富的中止,它会打印出出错代码的文件名和行号,就像assert()一样

这是编译器可以做的事情——正如您所说,编译器本质上是在指针被取消引用的任何位置自动插入
assert()
。这可能会大大增加调试构建的大小,但对于许多(或大多数)目的来说,这可能仍然是可以接受的。我认为这对于编译器来说是一个合理的选择


我不知道编译器供应商会怎么说。。。也许在上发布一个请求,看看他们怎么说。

我同意迈克尔·伯尔的观点,这实际上没有任何作用或帮助

此外,这对于悬空指针仍然不起作用,因为悬空指针往往比空指针更隐蔽,更难追踪


至少对于空指针,在撤销它们之前确保它们是有效的非常简单。

您的建议有几个主要问题:

您希望编译器检测哪些条件?在Linux/x86上,未对齐的访问可能导致
SIGBUS
,堆栈溢出可能导致
SIGSEGV
,但在这两种情况下,从技术上讲,都可以编写应用程序来检测这些条件并“正常”失败<可以检测到code>NULL指针检查,但最隐匿的错误是由于无效的指针访问,而不是
NULL
指针

<> P. C和C++编程语言提供足够的灵活性,因此如果给定的随机地址是任意类型的有效指针,则运行时不可能确定100%的成功。 当运行时环境检测到这种情况时,您希望它做什么?它无法纠正这种行为(除非你相信魔法)。它只能继续执行或退出。但是等一下。。。这就是信号发出时已经发生的事情!程序退出,生成核心转储,应用程序开发人员可以使用该核心转储来确定程序崩溃时的状态

您所提倡的实际上听起来像是希望在调试器(
gdb
)或通过某种形式的虚拟化(
valgrind
)运行应用程序。这已经是可能的了,但是默认情况下这样做是没有意义的,因为它对非开发人员没有任何好处

更新以回应评论:


没有理由修改调试版本的编译过程。如果需要应用程序的“温和”调试版本,则应在调试器中运行它。将可执行文件封装在一个透明的脚本中是非常容易的。

因此,您的意思是,在系统抛出错误之前,它应该向……抛出一个错误。。。。警告你即将发生的错误

重点是什么?当我犯了一个错误,我知道这意味着我犯了一个错误。我不需要一个单独的消息先说“你现在将得到一个SEGFULT”

我完全没有抓住要点吗p

编辑: 我明白你在编辑中的意思,但这并不容易实现。问题在于,如果您访问了一个错误的指针,那么应该发生什么事情并不是由编译器、语言或运行时决定的。官方语言对此没有做出任何承诺或保证。相反,操作系统会触发一个错误,而不知道这是一个调试可执行文件,也不知道是哪个行号触发了问题,或者其他任何事情。 这个错误唯一的意思是“你试图访问地址X,我不能允许。死”。编译器应该如何处理这个问题

那么谁应该生成这个有用的错误消息呢?怎么做? 当然,编译器可以做到这一点,但要在错误处理中包装每个指针访问,以确保如果发生segfault/access冲突,我们会捕获它,并触发断言。问题是,这将是可笑的缓慢。不仅仅是“发布太慢”,而是“太慢而无法使用”。它还假定编译器可以访问您调用的所有代码。如果调用第三方库中的函数会怎么样?内部的指针访问不能封装在错误处理代码中,因为编译器不会为该库生成代码

操作系统可以做到这一点,假设它愿意/能够加载相关的符号文件,以某种方式检测您是否正在运行调试可执行文件等等。
struct mystruct {
    int first;
    int second;
    int third;
    int fourth;
};