为什么释放无效指针在 C++ 中未定义?

Why is freeing invalid pointers left undefined in C++?

考虑以下程序:

#include <iostream>
int main()
{
    int b=3;
    int* a=&b;
    std::cout<<*a<<'\n';
    delete a;  // oops disaster at runtime undefined behavior
}

好的,根据 C++ 标准,程序的行为是未定义的。但我的问题是为什么它没有定义?为什么 C++ 的实现不给出任何编译器错误或任何警告?是否真的很难确定指针的有效性(意味着在编译时检查指针是否由 new 返回?)静态确定指针的有效性(即编译时)是否涉及任何开销?

不可能在编译时确定指针指向什么,这里有一个例子来说明这一点:

volatile bool newAlloc;

int main()
{
   int b=3;
   int* a;
   if(newAlloc)
   {
       a = new int;
   } else {
       a = &b;
   }
   std::cout<<*a<<'\n';
   delete a;  // impossible to know what a will be
}

在一般情况下不可能在编译时确定指针是否有效。例如,如果你有一个函数作为库的一部分,它接受一个指针作为参数,编译器不能确定它总是有一个有效的指针传递给它。

标准将删除无效指针保留为未定义行为,否则每次删除或取消引用指针时都必须在运行时进行检查,这会导致语言设计者没有的性能损失想要。

Why implementations of C++ don't give any compiler errors or any warnings?

Clang 静态分析器做到了。

对于您的代码片段,您可以获得:

$ scan-build clang++ main.cpp
scan-build: Using '/usr/bin/clang' for static analysis
main.cpp:7:5: warning: Argument to 'delete' is the address of the local variable 'b', which is not memory allocated by 'new'
    delete a;  // oops disaster at runtime undefined behavior
    ^~~~~~~~
1 warning generated.
scan-build: 1 bug found.

如前所述,并非总是可以在编译时确定指针是否有效,但静态分析器绝对可以帮助您。

这里是实现细节答案:

new/delete 实现必须有一些方法来跟踪关于通过 new 分配的所有块的一些数据,尤其是关于它们的大小。通常,这是通过将此数据存储在分配块开始 之前的字节 中来完成的。这样,delete 的实现可以非常快速地确定如何处理该块。当然,如果您传入无效地址,delete 实现只会在那里找到虚假数据,可能会导致您的应用程序崩溃或默默地破坏数据。但这没关系,因为传入无效地址是未定义的行为。

现在,让传递无效指针合法化会产生什么后果?
new/delete 实现不能只从传入的指针中查找此信息,因为那可能是虚假信息。检索必要信息的唯一方法是 在 table 当前有效指针 中查找给定指针。这样的查找是相当昂贵的,即使使用快速查找算法也是如此; 很多比取消引用给定的指针慢很多,无论如何。

所以这又是一个 speed/safety 权衡,C++ 在这个例子中选择了速度。