为什么不删除我的指针删除我的指针?

时间:2011-08-06 10:57:24

标签: c++ memory-leaks undefined-behavior

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

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

8 个答案:

答案 0 :(得分:25)

您正在导致未定义的行为。这意味着任何事情都可能发生。由于确实发生了某些事情,所有事情都表现得有记录。 (有时候“某些东西”看起来非常类似于你可能错过的其他东西。完全按照你认为你想要实现的目标是“未定义行为”可能允许的实例之一。)

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

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

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

答案 1 :(得分:23)

  

我必须做一些非常简单的事情。

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

当你做错事时,这是未定义的行为。这意味着一切都会发生。充其量,你的代码崩溃,段错误和闪烁的灯亮起来,说“你正在使用自由记忆”,一切都很清楚。最糟糕的是,demons fly out of your nose。但正好在这之上,第二个可能的结果就是一切看起来像你想要的那样有效。

答案 2 :(得分:5)

这将是内存泄漏:

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

分配内存而不再删除它。

答案 3 :(得分: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释放内存。在有人运行你的程序较长时间并发现它占用了系统的所有内存之前,通常不会注意到这一点。

答案 4 :(得分:3)

此代码

delete P1;

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

导致未定义的行为。所以一切都会发生。导致内存泄漏要容易得多:

for(;;) new int;

答案 5 :(得分:2)

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

快速测试显示以下行为:
MSVC2010:
debug build:* P1 = 0xfeeefeee
发布版本:* P1 =&lt; random&gt;
GCC 4.6:
调试&amp;发布版本:* P1 = 0

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

答案 6 :(得分:1)

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

答案 7 :(得分:0)

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

但这不是内存泄漏。当你指向另一个内存分配(或者只是丢弃指针)而不释放它指向的内存时。第一个然后保持分配,因为你没有引用它,你无法释放它。