【问题标题】:How to fix heap corruption如何修复堆损坏
【发布时间】:2009-10-14 16:45:42
【问题描述】:

我尝试构建一个非常简约的内存读取库,以从中读取一些unsigned ints。但是,当ReadUnsignedInt 方法想要返回时,我遇到了“HEAP CORRUPTION DETECTED”错误消息。

检测到堆损坏。 CRT 检测到应用程序在缓冲区结束后写入内存。

正如我所读到的,这可能是尝试双重删除某些内容时的原因。这可能是由于std::tr1::shared_ptr 的某些错误使用造成的,但我无法确定我对它们做错了什么。代码如下(错误处理省略):

unsigned int Memory::ReadUnsignedInt (unsigned int address) const {
    std::tr1::shared_ptr<byte> bytes = 
        this->ReadBytes(address, sizeof(unsigned int));
    return *((int*)bytes.get());
    // correct value (how to improve this ugly piece of code?)
}

std::tr1::shared_ptr<byte> Memory::ReadBytes (
    unsigned int address, int numberOfBytes) const
{
    std::tr1::shared_ptr<byte> pBuffer(new byte(numberOfBytes));
    ReadProcessMemory(m_hProcess.get(), (LPCVOID)address, 
        pBuffer.get(), numberOfBytes * sizeof(byte), NULL))
    return pBuffer;
}

【问题讨论】:

    标签: c++ shared-ptr raii


    【解决方案1】:

    Michael 和 Naveen 都在您的代码中发现了相同的主要缺陷,但不是唯一的缺陷。

    shared_ptrdelete 指向的对象在其引用计数变为零时。

    这意味着你只能给它分配new的对象——而不是new[]

    您可能希望改用shared_ptr&lt;vector&lt;byte&gt; &gt;boost::shared_array&lt;byte&gt;

    【讨论】:

    • 优秀的捕获 - 虽然这在大多数平台上都是一个无声的错误,因为 byte 没有需要调用的析构函数,但这里的正确答案是使用 vector 而不是 shared_ptr。跨度>
    • ...或 shared_array (我认为)。
    • 已更新。我正在研究如何将自定义析构函数添加到 shared_ptr,但这比它的价值更多。
    【解决方案2】:

    问题是:

    new byte(numberOfBytes)
    

    这会分配一个值为 numberOfBytes 的单个字节。

    你想做的:

    new byte[numberOfBytes]    
    

    它分配一个字节数组 numberOfBytes long。

    但是既然你知道你只读取 4 个字节,为什么还要费心分配内存呢?只需在堆栈上传递无符号 int 的地址即可。

    【讨论】:

      【解决方案3】:

      您的代码的基本问题已经指出。看着它,我想知道为什么你会在这里使用 shared_ptr 。如果我这样做,我可能会使用这样的东西:

      unsigned Memory::ReadUnsignedInt(unsigned address) { 
          unsigned ret;
          ReadProcessMemory(m_hProcess.get(), (void *)address, &ret, sizeof(ret), NULL);
          return ret;
      }
      
      std::vector<char> Memory::ReadBytes(unsigned address, int num) { 
          std::vector<char> ret(num);
          ReadProcessMemory(m_hProcess.get(), (void *)address, &ret[0], num, NULL);
          return ret;
      }
      

      再一次,我想使用模板而不是 ReadUnsignedInt:

      template <class T>
      T Memory::Read(unsigned address) { 
          T ret;
          ReadProcessMemory(m_hProcess.get(), (void*)address, &ret, sizeof(ret), NULL);
          return ret;
      }
      

      由于您没有传递可以从中推断出模板参数类型的参数,因此您始终必须明确指定:

      int x = Read<int>(wherever);
      char a = Read<char>(wherever);
      

      另一种方法是将目标作为参数传递:

      template <class T>
      Memory::Read(unsigned address, T &t) { 
          ReadProcessMemory(my_hProcess.get(), (void *)address, &t, sizeof(t), NULL);
      };
      

      你会这样使用:

      Read(wherever, some_int);
      Read(somewhere, some_long);
      

      等等。

      如果您担心返回 char 向量的低效率,您可能不应该 - VC++(像大多数其他合理的当前编译器一样)具有所谓的“命名返回值优化”,这意味着在某种情况下像这样,它将一个隐藏的引用传递给您将结果分配给的向量,ReadBytes 将使用它来将数据直接存放在它最终将要结束的地方。就此而言,只要开启任何合理的优化,ReadBytes 几乎肯定会以内联函数的形式结束,因此所涉及的任何内容都不会真正“通过”或“返回”。

      另一方面,这段代码在较旧的编译器上运行得不是特别好——而且对于足够旧的编译器,使用成员模板函数的版本甚至可能无法编译。但是,只要您使用当前合理的编译器,生活就应该是美好的。

      【讨论】:

        【解决方案4】:

        我认为new byte(numberOfBytes) 应该是new byte[numberOfBytes]。否则,您将只分配一个字节。只是为了完成答案,正如@ephemient 指出的那样,您不能在此处使用 shared_ptr ,因为它将执行delete ,而您应该执行delete[] 。如果不这样做,行为将是未定义的。

        【讨论】:

          猜你喜欢
          • 2019-12-01
          • 1970-01-01
          • 1970-01-01
          • 2020-10-15
          • 2015-03-04
          • 2011-09-04
          • 2010-11-23
          • 1970-01-01
          • 2010-11-15
          相关资源
          最近更新 更多