为什么释放无效指针在c++中没有定义

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

本文关键字:定义 c++ 释放 无效 指针 为什么      更新时间:2023-10-16

考虑以下程序:

#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
}

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

标准将无效指针的删除作为未定义行为,否则每次指针被删除或解引用时都必须在运行时进行检查,这将导致语言设计者不希望的性能损失。

为什么c++的实现不给出任何编译错误或任何警告?

Clang Static Analyzer完成。

对于你的一段代码,你可以得到:

$ 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.

如前所述,在编译时并不总是能够确定指针是否有效,但是Static Analyzer绝对可以帮助您解决这种情况。

下面是实现细节答案:

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

现在,使传递无效指针合法化的后果是什么?
new/delete实现将无法从传入的指针中查找此信息,因为这可能是虚假信息。获取必要信息的唯一方法是在当前有效指针的表中查找给定的指针。这样的查找是相当昂贵的,即使使用快速查找算法;比解引用给定的指针要慢得多。

所以这又是关于速度和安全的权衡,c++在这个例子中选择了速度。