为什么删除指针不会使其变得不可用?

问题描述 投票:0回答:8

所以为了更好地理解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

我在课堂上遇到了更复杂的事情,但这个例子说明了我的失败。我认为删除需要一个指针并释放它的内存,从而使指针或至少它指向的内容无效?我一定是在做一些非常简单非常错误的事情。

c++ pointers memory-leaks undefined-behavior delete-operator
8个回答
26
投票

你正在造成未定义的行为。这意味着任何事情都可能发生。由于确实发生了某些事情,所以一切都按照记录进行。 (有时“某事”看起来与您可能错误期望的其他事情非常相似。完全按照您认为自己想要实现的目标进行操作是“未定义行为”可能允许的实例之一。)

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

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

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

23
投票

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

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

当你做错事时,这是未定义的行为。这意味着任何事情都可能发生。最好的情况是,你的代码崩溃,出现段错误,并且闪烁的灯亮起,表示“你正在使用释放的内存”,一切都非常清楚。最坏的情况下,恶魔会从你的鼻子里飞出来。但除此之外,第二个最坏的可能结果是一切似乎都按您的预期进行。


5
投票

这将是内存泄漏:

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

分配内存而不再次删除。


4
投票

这段代码

delete P1;

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

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

for(;;) new int;

4
投票
// 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
释放内存。除非有人长时间运行您的程序并发现它耗尽了系统的所有内存,否则通常不会注意到这一点。


2
投票

取消引用已删除的指针是未定义的行为,正如已经解释的那样,即您做错了什么。编译器可能会通过将指针的值更改为某个“magic”值来“帮助”您发现该错误,该值总是会导致取消引用指针失败,并且可以给您一个线索,表明这是因为它之前已被删除。 快速测试显示了我的以下行为: MSVC2010: 调试版本:*P1 = 0xfeeefeee 发布版本:*P1 =

海湾合作委员会 4.6:
调试和发布版本:*P1 = 0

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

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


1
投票
删除不会使指针无效。它释放它指向的内存返回堆。在这种情况下,内存没有被重用,因此仍然包含相同的内容。在某些时候,内存将被重用,然后你就会得到其他人所说的未定义的行为。

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

0
投票

© www.soinside.com 2019 - 2024. All rights reserved.