删除字符串时堆损坏

时间:2010-11-18 21:06:02

标签: c++ string stl heap cstring

这是我的代码:

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;
 }

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

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

我的问题可能很明显,但为什么我会遇到堆腐败?当我创建一个std :: string时,它应该复制字符串,我应该可以安全地删除c字符串。

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

有什么想法吗?

6 个答案:

答案 0 :(得分:8)

您正在访问字符串的保留字节。您保留了strLen个字符,但在\0字符处添加了strLen。从0开始计数为C数组,字符strLen位于strLen + 1位置,因此您在字符串的保留空间之外放置一个值。您应该在strLen + 1的第二行预留main,以便您的代码能够正常运行。

答案 1 :(得分:4)

变化:

char* rawString = new char[strLen];

为:

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

答案 2 :(得分:2)

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

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

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

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

std::string retVal(rawString, strlen);

答案 3 :(得分:1)

由于数组在c ++中是0索引的,当你创建一个大小为strLen的数组,然后在位置strLen放置一个0时,你在数组结束后写的是0分配

答案 4 :(得分:1)

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

有两种方法可以避免使用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;
}

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

答案 5 :(得分:0)

 rawString[strLen] = '\0';

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

如果strLen为10,则为10个字符分配空格,读取10个字符,并将此NUL写入位置11. Ooops