4

今天,在 EFNet C++ Wiki 上的文章heap corruption中,我发现了两段代码。

void this_is_bad() /* You wouldn't believe how often this kind of code can be found */    
{    
    char *p = new char[5];    /* spend some cycles in the memory manager */    
    /* do some stuff with p */    
    delete[] p;      /* spend some more cycles, and create an opportunity for a leak */    
 }  

替代方式:

void this_is_good()    
{    
   /* Avoid allocation of small temporary objects on the heap*/   
   char p[5];    /* Use the stack instead */   
   /* do some stuff */  
}    

有人可以帮助我理解为什么第一段代码不被认为是好的吗?

4

4 回答 4

7

使用时char* p,您在p堆上进行分配,因此您必须注意在最后删除它。在char *p和之间deletedo some stuff with p代码可能会抛出异常并被p泄露。

使用 时char p[5],您可以p在堆栈上进行分配,而无需照顾,delete即使代码抛出异常,您也是安全的。

void this_is_bad()   
{    
  char *p = new char[5]; //on the heap
  // What happens if I throw an unhandled exception here?
  delete[] p;  // I never get to delete p and it gets leaked
}  
于 2012-06-14T15:51:28.130 回答
2

当您使用堆栈而不是堆时,一旦当前函数的范围丢失,内存就会恢复。

使用new关键字时,您分配堆内存。您必须记住删除使用new关键字分配的所有内存。new如果在关键字之后和关键字之前引发异常delete,您可能会造成内存泄漏,因为您可能无法在引发异常之后的点恢复执行。

于 2012-06-14T15:51:44.797 回答
2

目前最好的方法是:

 #include <vector>

 void this_is_great()
 {
     std::vector<char> myCharVec(5);

     // use myCharVec

 }  // when this function ends whether by return or by exception myCharVec is cleaned up

这样,向量中的内存(想想“数组”)在堆上,但是对象存在并且一些簿记在堆栈上(粗略地说),当对象被破坏时,它的堆内存被自动清理向量的析构函数,没有垃圾收集开销等。

这就是所谓的RAII成语

将其直接放在堆栈上的另一个原因是堆栈上的缓冲区溢出(在处理数组时经常发生)可能比内存在堆上更具破坏性和更难检测。

于 2012-06-14T16:06:41.410 回答
2

堆是共享内存资源,必须在进程外部进行管理(由内存管理器,如示例中所述)。另一方面,堆栈由您的进程管理,并且当您的方法完成时,在您的方法中推入堆栈的任何变量都会自动释放/弹出。这是干净的,几乎免费的,而且几乎是万无一失的。

这避免了创建内存泄漏的可能性 - 传递给“删除”的区域(例如,通过无意中重新分配 ptr 值)与通过其“新”操作分配的内存不完全匹配。在方法中使用非静态变量时,没有理由不这样做。

另请参阅: C++、自由存储与堆

于 2012-06-14T16:08:15.200 回答