双删除会发生什么?

pur*_*uck 20 c++ pointers memory-leaks

Obj *op = new Obj;
Obj *op2 = op;
delete op;
delete op2; // What happens here?
Run Code Online (Sandbox Code Playgroud)

当你意外地双重删除时,最糟糕的情况是什么?有关系吗?编译器会抛出错误吗?

Ben*_*igt 22

未定义的行为.标准没有任何保证.可能你的操作系统提供了一些保证,比如"你不会破坏另一个进程",但这对你的程序没什么帮助.

你的程序可能会崩溃.您的数据可能已损坏.直接存入您的下一个薪水可能会从您的帐户中扣除500万美元.

  • 或者它可以用您的信用卡订购披萨. (27认同)
  • 它可以修理我的车吗? (2认同)

Car*_*rum 21

它会导致未定义的行为.任何事情都可能发生.在实践中,运行时崩溃可能是我所期望的.

  • 随机小狗也可能死. (20认同)
  • 有一种已知的案例,由于未定义的行为,人员监视着火. (3认同)
  • 我尝试了 OP 的示例,只是为了查看结果。我没有遇到运行时崩溃。另一方面,我的办公楼开始呼吸奶酪。请注意:花粉可能会减少! (3认同)
  • 如果您非常幸运,您只会立即发生运行时崩溃。更常见的是,您会删除一些其他无辜的对象或已重用的垃圾内存,从而杀死小狗。(也许在你的程序做了一些奇怪的事情之前完全无法检测到,而这些事情可能不会在未来某个未知的时间崩溃。)最糟糕的部分是它会级联——如果双重删除确实成功删除了另一个意外的对象,那么最终该对象确实达到了有意删除的目的,这将是另一个双重删除,并且您已经杀死了更多的小狗。 (2认同)

Rus*_*ove 17

它是未定义的行为,因此实际结果将根据编译器和运行时环境而有所不同.

在大多数情况下,编译器不会注意到.在许多(如果不是大多数)情况下,运行时内存管理库将崩溃.

在引擎盖下,任何内存管理器都必须维护一些关于它分配的每个数据块的元数据,以允许它从malloc/new返回的指针中查找元数据.通常,这采用在分配块之前的固定偏移处的结构的形式.这个结构可以包含一个"幻数" - 一个不太可能通过纯粹机会发生的常数.如果内存管理器在预期的位置看到幻数,它知道提供给free/delete的指针很可能是有效的.如果它没有看到幻数,或者它看到不同的数字意味着"此指针最近被释放",它可以默默地忽略空闲请求,或者它可以打印有用的消息并中止.根据规范,这两种方法都是合法的,并且这两种方法都有赞成/反对意见.

如果内存管理器没有在元数据块中保留幻数,或者没有检查元数据的完整性,那么任何事情都可能发生.根据内存管理器的实现方式,结果很可能是没有有用消息的崩溃,无论是在内存管理器逻辑中,还是在下次内存管理器尝试分配或释放内存时稍晚,或者更晚以后当程序的两个不同部分都认为他们拥有相同的内存块时.

我们来试试吧.将您的代码转换为so.cpp中的完整程序:

class Obj
{
public:
    int x;
};

int main( int argc, char* argv[] )
{
    Obj *op = new Obj;
    Obj *op2 = op;
    delete op;
    delete op2;

    return 0;
}
Run Code Online (Sandbox Code Playgroud)

编译它(我在OSX 10.6.8上使用gcc 4.2.1,但是YMMV):

russell@Silverback ~: g++ so.cpp
Run Code Online (Sandbox Code Playgroud)

运行:

russell@Silverback ~: ./a.out
a.out(1965) malloc: *** error for object 0x100100080: pointer being freed was not allocated
*** set a breakpoint in malloc_error_break to debug
Abort trap
Run Code Online (Sandbox Code Playgroud)

在那里,gcc运行时实际上检测到它是双重删除,并且在它崩溃之前相当有用.

  • 在这里你去@Russell,你的优秀评论的赞成;)哦,如果你不认为C++"真正的编程语言",特此邀请你将它添加到你忽略的标签列表.之后,我们将聚在一起,用十六进制编辑器生成一些机器代码. (3认同)
  • 我很确定其他答案已经很好地涵盖了“未定义的行为”。 (2认同)

Enn*_*ael 7

虽然这是未定义的:

int* a = new int;
delete a;
delete a; // same as your code
Run Code Online (Sandbox Code Playgroud)

这是明确定义的:

int* a = new int;
delete a;
a = nullptr; // or just NULL or 0 if your compiler doesn't support c++11
delete a; // nothing happens!
Run Code Online (Sandbox Code Playgroud)

我想我应该发布它,因为没有其他人提到它。

  • @Vlad删除后指针的值与删除前相同 - 换句话说,指针内的*地址*仍然相同,只是该地址处的数据现已被释放。(因此,尝试通过取消引用指针来访问该数据是未定义的行为。)编译器不自动将 nullptr 分配给已删除的指针的原因可能是出于历史原因和性能原因。 (2认同)