C++ 双重删除会发生什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/9169774/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What happens in a double delete?
提问by purepureluck
Obj *op = new Obj;
Obj *op2 = op;
delete op;
delete op2; // What happens here?
What's the worst that can happen when you accidentally double delete? Does it matter? Is the compiler going to throw an error?
当您不小心重复删除时,最糟糕的情况是什么?有关系吗?编译器会抛出错误吗?
回答by Carl Norum
It causes undefined behaviour. Anything can happen. In practice, a runtime crash is probably what I'd expect.
它会导致未定义的行为。任何事情都可能发生。在实践中,运行时崩溃可能是我所期望的。
回答by Ben Voigt
Undefined behavior. There are no guarantees whatsoever made by the standard. Probably your operating system makes some guarantees, like "you won't corrupt another process", but that doesn't help your program very much.
未定义的行为。标准不作任何保证。可能你的操作系统做出了一些保证,比如“你不会破坏另一个进程”,但这对你的程序没有多大帮助。
Your program could crash. Your data could be corrupted. The direct deposit of your next paycheck could instead take 5 million dollars out of your account.
你的程序可能会崩溃。您的数据可能已损坏。直接存入您的下一份薪水可能会从您的帐户中扣除 500 万美元。
回答by Russell Borogove
It's undefined behavior, so the actual result will vary depending on the compiler & runtime environment.
这是未定义的行为,因此实际结果会因编译器和运行时环境而异。
In most cases, the compiler won't notice. In many, if not most, cases, the runtime memory management library will crash.
大多数情况下,编译器不会注意到。在许多(如果不是大多数)情况下,运行时内存管理库会崩溃。
Under the hood, any memory manager has to maintain some metadata about each block of data it allocates, in a way that allows it to look up the metadata from the pointer that malloc/new returned. Typically this takes the form of a structure at fixed offset before the allocated block. This structure can contain a "magic number" -- a constant that is unlikely to occur by pure chance. If the memory manager sees the magic number in the expected place, it knows that the pointer provided to free/delete is most likely valid. If it doesn't see the magic number, or if it sees a different number that means "this pointer was recently freed", it can either silently ignore the free request, or it can print a helpful message and abort. Either is legal under the spec, and there are pro/con arguments to either approach.
在幕后,任何内存管理器都必须维护一些关于它分配的每个数据块的元数据,以允许它从 malloc/new 返回的指针中查找元数据。通常,这采用分配块之前固定偏移量的结构形式。这个结构可以包含一个“幻数”——一个不太可能偶然发生的常数。如果内存管理器在预期的位置看到幻数,它就知道提供给 free/delete 的指针很可能是有效的。如果它没有看到幻数,或者看到一个不同的数字,表示“这个指针最近被释放”,它可以默默地忽略释放请求,或者它可以打印一条有用的消息并中止。根据规范,这两种方法都是合法的,两种方法都有赞成/反对的论据。
If the memory manager doesn't keep a magic number in the metadata block, or doesn't otherwise check the sanity of the metadata, then anything can happen. Depending on how the memory manager is implemented, the result is most likely a crash without a helpful message, either immediately in the memory manager logic, somewhat later the next time the memory manager tries to allocate or free memory, or much later and far away when two different parts of the program each think they have ownership of the same chunk of memory.
如果内存管理器没有在元数据块中保存幻数,或者没有检查元数据的完整性,那么任何事情都可能发生。根据内存管理器的实现方式,结果很可能是在没有帮助消息的情况下崩溃,要么立即在内存管理器逻辑中,要么在下次内存管理器尝试分配或释放内存时稍晚,要么在很久以后很远当程序的两个不同部分都认为它们拥有相同的内存块时。
Let's try it. Turn your code into a complete program in so.cpp:
让我们试试吧。在 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;
}
Compile it (I'm using gcc 4.2.1 on OSX 10.6.8, but YMMV):
编译它(我在 OSX 10.6.8 上使用 gcc 4.2.1,但 YMMV):
russell@Silverback ~: g++ so.cpp
Run it:
运行:
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
Lookie there, the gcc runtime actually detects that it was a double delete and is fairly helpful before it crashes.
看那里,gcc 运行时实际上检测到它是双重删除,并且在崩溃之前非常有用。
回答by Enn Michael
While this is undefined:
虽然这是未定义的:
int* a = new int;
delete a;
delete a; // same as your code
this is well defined:
这是明确定义的:
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!
Thought I should post it since no one else was mentioning it.
我想我应该发布它,因为没有其他人提到它。
回答by Heptic
The compiler maygive a warning or something, especially in obvious (like in your example) but it is not possible for it to always detect. (You can use something like valgrind, which at runtime can detect it though). As for the behaviour, it can be anything. Some safe library might check, and handle it fine -- but other runtimes (for speed) will make the assumption you call is correct (which it's not) and then crash or worse. The runtime is allowed to make the assumption you're not double deleting (even if double deleting would do something bad, e.g. crashing up your computer)
编译器可能会发出警告或其他警告,尤其是明显的(例如在您的示例中),但它不可能始终检测到。(您可以使用 valgrind 之类的东西,但它在运行时可以检测到它)。至于行为,它可以是任何东西。一些安全的库可能会检查并处理它——但其他运行时(为了速度)会假设你调用的是正确的(它不是)然后崩溃或更糟。运行时可以假设您不是双重删除(即使双重删除会做坏事,例如使您的计算机崩溃)
回答by Petr
Everyone already told you that you shouldn't do this and that it will cause undefined behavior. That is widely known, so let's elaborate on this on a lower level and let's see what actually happens.
每个人都已经告诉过你,你不应该这样做,这会导致未定义的行为。这是众所周知的,所以让我们在较低级别详细说明这一点,让我们看看实际发生了什么。
Standard universal answer is that anything can happen, that's not entirely true. For example, the computer will not attempt to kill you for doing this (unless you are programming AI for a robot) :)
标准普遍答案是任何事情都可能发生,这并不完全正确。例如,计算机不会因为你这样做而试图杀死你(除非你正在为机器人编程 AI):)
The reason why there can't be any universal answer is that as this is undefined, it may differ from compiler to compiler and even across different versions of same compiler.
之所以没有任何通用答案,是因为这是未定义的,它可能因编译器而异,甚至在同一编译器的不同版本之间也可能不同。
But this is what "roughly" happens in most cases:
但在大多数情况下,这就是“大致”发生的情况:
delete
consist of 2 primary operations:
delete
由 2 个主要操作组成:
- it calls the destructor if it's defined
- it somehow frees the memory allocated to the object
- 如果已定义,则调用析构函数
- 它以某种方式释放分配给对象的内存
So, if your destructor contains any code that access any data of class that already was deleted, it may segfault OR (most likely) you will read some nonsense data. If these deleted data are pointers then it will most likely segfault, because you will attempt to access memory that contains something else, or doesn't belong to you.
因此,如果您的析构函数包含访问已删除类的任何数据的任何代码,则它可能会出现段错误或(最有可能)您将读取一些无意义的数据。如果这些删除的数据是指针,那么它很可能会出现段错误,因为您将尝试访问包含其他内容或不属于您的内存。
If your constructor doesn't touch any data or isn't present (let's not consider virtual destructors here for simplicity), it may not be a reason for crash in most compiler implementations. However, calling a destructor is not the only operation that is going to happen here.
如果您的构造函数没有接触任何数据或不存在(为了简单起见,我们在这里不考虑虚拟析构函数),这可能不是大多数编译器实现中崩溃的原因。然而,调用析构函数并不是这里将要发生的唯一操作。
The memory needs to be free'd. How it's done depends on implementation in compiler, but it may as well execute some free
like function, giving it the pointer and size of your object. Calling free
on memory that was already deleted may crash, because the memory may not belong to you anymore. If it does belong to you, it may not crash immediately, but it may overwrite memory that was already allocated for some different object of your program.
内存需要被释放。它是如何完成的取决于编译器中的实现,但它也可以执行一些free
类似的函数,给它你的对象的指针和大小。调用free
已经删除的内存可能会崩溃,因为内存可能不再属于你。如果它确实属于您,它可能不会立即崩溃,但它可能会覆盖已经为程序的某些不同对象分配的内存。
That means one or more of your memory structures just got corrupted and your program will likely crash sooner or later or it might behave incredibly weirdly. The reasons will not be obvious in your debugger and you may spend weeks figuring out what the hell just happened.
这意味着你的一个或多个内存结构刚刚被破坏,你的程序迟早可能会崩溃,或者它可能表现得非常奇怪。原因在您的调试器中不会很明显,您可能会花费数周时间弄清楚刚刚发生了什么。
So, as others have said, it's generally a bad idea, but I suppose you already know that. Don't worry though, innocent kitten will most likely not die if you delete an object twice.
所以,正如其他人所说,这通常是一个坏主意,但我想你已经知道了。不过别担心,如果你删除一个对象两次,无辜的小猫很可能不会死。
Here is example code that is wrong but may work just fine as well (it works OK with GCC on linux):
这是错误的示例代码,但也可以正常工作(它在 Linux 上与 GCC 一起工作正常):
class a {};
int main()
{
a *test = new a();
delete test;
a *test2 = new a();
delete test;
return 0;
}
If I don't create intermediate instance of that class between deletes, 2 calls to free on same memory happens as expected:
如果我不在删除之间创建该类的中间实例,则会按预期在同一内存上调用 2 次:
*** Error in `./a.out': double free or corruption (fasttop): 0x000000000111a010 ***
To answer your questions directly:
直接回答您的问题:
What is the worst that can happen:
可能发生的最坏情况是什么:
In theory, your program causes something fatal. It may even randomly attempt to wipe your hard drive in some extreme cases. The chances depends on what your program actually is (kernel driver? user space program?).
理论上,你的程序会导致一些致命的事情。在某些极端情况下,它甚至可能会随机尝试擦除您的硬盘。机会取决于您的程序实际上是什么(内核驱动程序?用户空间程序?)。
In practice, it would most likely just crash with segfault. But something worse might happen.
在实践中,它很可能会因段错误而崩溃。但更糟糕的事情可能会发生。
Is the compiler going to throw an error?
编译器会抛出错误吗?
It shouldn't.
它不应该。
回答by Destructor
No, it isn't safe to delete the same pointer twice. It is undefined behaviour according to C++ standard.
不,两次删除同一个指针是不安全的。根据 C++ 标准,这是未定义的行为。
From the C++ FAQ: visit this link
来自 C++ 常见问题解答:访问此链接
Is it safe to delete the same pointer twice?
No! (Assuming you didn't get that pointer back from new in between.)
两次删除同一个指针是否安全?
不!(假设您没有从 new 中间取回该指针。)
For example, the following is a disaster:
例如,以下是灾难:
class Foo { /*...*/ };
void yourCode()
{
Foo* p = new Foo();
delete p;
delete p; // DISASTER!
// ...
}
That second delete p line might do some really bad things to you. It might, depending on the phase of the moon, corrupt your heap, crash your program, make arbitrary and bizarre changes to objects that are already out there on the heap, etc. Unfortunately these symptoms can appear and disappear randomly. According to Murphy's law, you'll be hit the hardest at the worst possible moment (when the customer is looking, when a high-value transaction is trying to post, etc.). Note: some runtime systems will protect you from certain very simple cases of double delete. Depending on the details, you might be okay if you happen to be running on one of those systems and if no one ever deploys your code on another system that handles things differently and if you are deleting something that doesn't have a destructor and if you don't do anything significant between the two deletes and if no one ever changes your code to do something significant between the two deletes and if your thread scheduler (over which you likely have no control!) doesn't happen to swap threads between the two deletes and if, and if, and if. So back to Murphy: since it can go wrong, it will, and it will go wrong at the worst possible moment. A non-crash doesn't prove the absence of a bug; it merely fails to prove the presence of a bug. Trust me: double-delete is bad, bad, bad. Just say no.
第二个 delete p 行可能对你做一些非常糟糕的事情。根据月相的不同,它可能会破坏您的堆、使您的程序崩溃、对堆上已经存在的对象进行任意和奇怪的更改等。不幸的是,这些症状可能会随机出现和消失。根据墨菲定律,您将在最糟糕的时刻受到最严重的打击(当客户正在寻找时,当高价值交易试图发布时,等等)。注意:某些运行时系统会保护您免受某些非常简单的双重删除情况的影响。根据详细信息,如果您碰巧在其中一个系统上运行,并且没有人将您的代码部署在另一个以不同方式处理事物的系统上,并且您正在删除没有析构函数的内容以及如果你不 不要在两次删除之间做任何重要的事情,并且如果没有人更改您的代码以在两次删除之间做一些重要的事情,并且您的线程调度程序(您可能无法控制!)没有碰巧在两次删除之间交换线程如果,如果,如果。所以回到墨菲:既然它可能出错,它就会出错,而且它会在最糟糕的时刻出错。非崩溃并不能证明不存在错误;它只是无法证明存在错误。相信我:双重删除很糟糕,很糟糕,很糟糕。拒绝吧。因为它可能会出错,它会出错,而且会在最糟糕的时刻出错。非崩溃并不能证明不存在错误;它只是无法证明存在错误。相信我:双重删除很糟糕,很糟糕,很糟糕。拒绝吧。因为它可能会出错,它会出错,而且会在最糟糕的时刻出错。非崩溃并不能证明不存在错误;它只是无法证明存在错误。相信我:双重删除很糟糕,很糟糕,很糟糕。拒绝吧。