为什么删除指针不会删除指针?

Why doesn't deleting my pointer delete my pointer?

本文关键字:删除 指针 为什么      更新时间:2023-10-16

所以为了更好地理解new/delete(真的用小例子向自己证明为什么接口需要虚拟析构函数(,我想了解内存泄漏,这样我就可以生活在对它们的恐惧中。 但是可以这么说,我很难泄漏; 实际上,我也很难使用new/delete。

这是我最简单的版本:

int* P1 = new int(43);
cout<<"P1 = "<<P1<<endl;
cout<<"*P1 = "<<*P1<<endl;
delete P1;
cout<<"P1 = "<<P1<<endl;
cout<<"*P1 = "<<*P1<<endl;

这将打印:

P1 = 0xcc0340
*P1 = 43
P1 = 0xcc0340
*P1 = 43

我在类中遇到了更复杂的东西,但这个例子说明了我的失败。 我以为删除会占用指针并释放其内存,从而使指针无效或至少使它指向的内容无效? 我一定是做了一件非常简单的事情,非常错误。

您正在导致未定义的行为。这意味着任何事情都可能发生。由于确实发生了一些事情,因此一切都按照记录的那样运行。(有时"某物"看起来与您可能错误期望的其他事物非常相似。完全按照你认为你想要实现的事情是"未定义行为"的可能允许实例之一。

另请注意,"内存泄漏"与您尝试执行的操作相反 - 在内存泄漏中,您忘记释放内存,而您已经释放了内存,现在正在访问无效内存。

这是一个真正的内存泄漏,它也不会导致未定义的行为 - 不要将"坏但正确"与"不正确"编程混淆!

int * factorial(int * n)
{
  if (*n == 0) return new int(1);
  else return new int(*n * *factorial(*n - 1));
}

我一定是做了一件非常简单的事情,非常错误。

你是绝对正确的。你的代码正在做一些非常简单、非常错误的事情,你已经付出了最终的代价——没有看到任何负面的结果。有时候,你做错了事,并没有发生任何坏事。这是最糟糕的结果,因为你可能没有意识到你的代码是错误的,你会继续使用它,直到有一天你的代码意外中断,你不知道在哪里或如何工作,因为它以前总是有效的。它会在与实际错误部分完全无关的地方损坏,您将花费数小时试图追踪它并找出它损坏的原因。

当你做错事时,这是未定义的行为。这意味着任何事情都可能发生。充其量,你的代码崩溃,段错误和闪烁的灯亮起说"你正在使用释放的内存",一切都非常清楚。在最坏的情况下,恶魔会从你的鼻子里飞出来。但除此之外,第二个最糟糕的结果是一切似乎都按照你的预期工作。

这将是内存泄漏:

int* P1 = new int(43);
     P1 = new int(42);

分配内存而不再次删除它。

此代码

delete P1;
cout<<"P1 = "<<P1<<endl;
cout<<"*P1 = "<<*P1<<endl;

导致未定义的行为。所以任何事情都可能发生。导致内存泄漏要容易得多:

for(;;) new int;
// Reserve some memory for an int and set that memory to the value 43.
int* P1 = new int(43);
// Print the address of the reserved memory.
cout<<"P1 = "<<P1<<endl;
// Print the contents of that memory.
cout<<"*P1 = "<<*P1<<endl;
// Free the memory - it is no longer reserved to you.
delete P1;
// int* P2 = new int(47);    
// Print the address of the memory. It still holds the address to 
// the memory that used to be reserved for you.
cout<<"P1 = "<<P1<<endl;
// Print the current value of the memory that used to be reserved.
cout<<"*P1 = "<<*P1<<endl;

如果您取消注释 P2 行,很可能会为其分配相同的内存,这会更改最后一行打印的值。

正如其他人指出的那样,访问已释放delete的内存会导致未定义的行为。未定义包括在某些情况下以奇怪的方式崩溃(也许仅在满月期间?;-(。它还包括目前运行良好的所有内容,但是该错误是一个地雷,每当您在程序的其他任何地方进行其他更改时,它就会跳闸。

记忆韭菜是指你用new分配内存,从不用delete释放它。这通常不会被注意到,直到有人运行您的程序较长时间并发现它占用了系统的所有内存。

如前所述,取消引用已删除的指针是未定义的行为,即您做错了什么。编译器可以通过将指针的值更改为某个魔术值来帮助您找到该错误,该值始终会导致取消引用指针失败,并可以为您提供提示,表明这是因为它以前被删除了。

快速测试为我显示了以下行为:
MSVC2010:
调试版本:*P1 = 0xfeeefeee
发布版本:*P1 = <随机>
海湾合作委员会 4.6:
调试和发布版本:*P1 = 0

所以你只是(不(幸运,你又得到了原来的值。我建议您始终以调试模式构建以测试程序(即使您没有调试(,因为它会为您的代码添加额外的健全性检查。

内存已释放,但未清理。该值可能会保留在内存中,直到其他某个进程在该位置写入新值。

删除不会

使指针失效。 它将指向的内存释放回堆。 在这种情况下,内存尚未重复使用,因此仍包含相同的内容。 在某个时候,内存将被重用,然后你会得到其他人所说的未定义的行为。

不过,这不是内存泄漏。 这是指将指针指向另一个内存分配(或只是丢弃指针(,而不释放它指向的内存。 然后,第一个保持分配状态,并且由于您没有对它的引用,因此无法释放它。