【问题标题】:Avoiding heap corruption避免堆损坏
【发布时间】:2012-06-17 16:50:38
【问题描述】:

今天,在 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 */  
}    

谁能帮我理解为什么第一段代码不被认为是好的?

【问题讨论】:

  • 在第一个 sn-p 中编写 cmets 的人让我觉得不专业。很好的一点是,5 字节的分配在堆栈上通常比在堆上更有意义,并且异常安全性也很好,但是可以通过较少轻率的评论来更好地说明这一点(以“你不会相信”是一个不好的迹象)。
  • 顺便说一下,搜索 cmets 看起来不是维基百科而是这个站点:efnetcpp.org/wiki/Heap_Corruption
  • 我的错,其实我很着急。我读了维基。

标签: c++ pointers memory operating-system heap-memory


【解决方案1】:

使用char* p 时,您在堆上分配p,因此您必须注意在最后删除它。在char *pdelete 之间,在do 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
}  

【讨论】:

  • cmets 还试图传达newdelete 不是免费操作。而在堆栈上分配数组通常意味着从堆栈指针寄存器中减去,这非常快。
【解决方案2】:

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

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

【讨论】:

    【解决方案3】:

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

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

    另见: C++, Free-Store vs Heap

    【讨论】:

      【解决方案4】:

      目前最好的方法是:

       #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 idiom

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2021-03-16
        • 2010-12-24
        • 2010-11-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2011-12-03
        • 2011-08-03
        相关资源
        最近更新 更多