【问题标题】:Heap corruption when deleting a string删除字符串时堆损坏
【发布时间】:2011-05-12 07:11:26
【问题描述】:

这是我的代码:

std::string readString()
{
     int strLen = Read<int>();
     char* rawString = new char[strLen];
     Read(rawString, strLen);
     rawString[strLen] = '\0';
     std::string retVal(rawString);
     delete [] rawString;
     return retVal;
 }

第一行读取字符串的长度。
第二行使用字符串长度创建一个新的字符数组(c-string)
第三行读取字符串(从文件中读取)
第 4 行在末尾添加 NULL。
第 5 行从 c 字符串中创建了一个 std::string。
第 6 行删除 c 字符串 (HEAP CORRUPTION HAPPENS HERE)
第 7 行返回字符串,但由于出错,它从未到达这一点。

在第 6 行出现堆损坏错误: CRT 检测到应用程序在堆缓冲区结束后写入内存。

我的问题可能很明显,但为什么会出现堆损坏?当我创建一个 std::string 时,它应该复制字符串,并且我应该可以安全地删除 c-string。

目前,我怀疑 std::string 在我删除它后试图访问它。

有什么想法吗?

【问题讨论】:

  • 您的代码中有delete[],因此您的代码很糟糕。使用std::vector 之类的,甚至直接读入字符串。
  • @GMan:在我发布之前甚至没有看到您的评论:p 它还如何简化代码真是令人难以置信......

标签: c++ string stl c-strings heap-corruption


【解决方案1】:

您正在访问字符串的保留字节。您保留了strLen 字符,但在字符strLen 处放置了\0。从 0 开始计数为 C 数组,字符 strLen 位于位置 strLen + 1,因此您将值放在字符串的保留空间之外。您应该在 main 的第二行保留 strLen + 1 以使您的代码正常工作。

【讨论】:

    【解决方案2】:

    变化:

    char* rawString = new char[strLen];
    

    到:

    char* rawString = new char[strLen + 1];
    

    【讨论】:

    • 谢谢。我不敢相信我忽略了这样的事情。我已经使用 C++ 一段时间了:)。也感谢其他为此提供帮助的人!
    【解决方案3】:

    int strLen = Read&lt;int&gt;() 可能只返回非空终止字符串的长度,当您尝试将\0 字节写入字符串时,会遇到缓冲区溢出问题。

    您应该检查strLen 是什么,并且很可能您必须像这样分配:

    char *rawString = new char[strlen+1];
    

    或者像这样使用std::string(const char *, size_t n)的重载构造函数:

    std::string retVal(rawString, strlen);
    

    【讨论】:

      【解决方案4】:

      由于数组在 C++ 中是从 0 开始索引的,因此当您创建大小为 strLen 的数组,然后在位置 strLen 处放置一个 0 时,您将在分配的数组末尾写一个零。

      【讨论】:

        【解决方案5】:

        到目前为止有很多建议,但没有一个解决异常安全问题:您如何摆脱潜在的内存泄漏?

        有两种方法可以避免使用new 进行分配(从而面临内存泄漏)。第一个非常简单,它使用称为 VLA 的可变长度数组的编译器扩展:

        std::string readString()
        {
          int strLen = Read<int>();
          char rawString[strLen+1]; // VLA: the length is determined at runtime
                                    // but the array is nonetheless on the stack
          Read(rawString, strLen);
          rawString[strLen] = '\0';
        
          std::string retVal(rawString);
          return retVal;
        }
        

        另一个符合标准:string 有一个可以访问的内部缓冲区(感谢 GMan,data 不是正确的访问方法)

        std::string readString()
        {
          int strLen = Read<int>();
        
          std::string retVal(strLen, '\0'); // no need to allocate extra space
        
          Read(&retVal[0], strLen);      // &retVal[0] gives access to the buffer
        
          return retVal;
        }
        

        我确实相信最后一个版本要好得多。不再涉及任何复制:)

        【讨论】:

        • 前者实际上是非标准C++。第二个只提供 const 访问权限。 :S 您想读入vector 或保留字符串并读入&amp;retVal[0],这会吐出一个连续的缓冲区。哦,你有“非常简单”。 :)
        • @GMan:废话,我以为data 有两个版本。我确实准确地说前者是非标准的,但我确实准确地说它是一个编译器扩展(而且是一个不错的扩展......)
        【解决方案6】:
         rawString[strLen] = '\0';
        

        将 NUL 从您分配的空间的末尾写入。

        如果 strLen 为 10,则为 10 个字符分配空间,读取 10 个字符,并将此 NUL 写入位置 11。哎呀

        【讨论】:

          猜你喜欢
          • 2013-06-04
          • 1970-01-01
          • 2013-04-20
          • 1970-01-01
          • 1970-01-01
          • 2013-01-23
          • 2015-08-14
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多