2

这是我的代码:

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)
第三行读取字符串(从文件中读取)
第四行在末尾添加一个 NULL。
第 5 行从 c 字符串中创建了一个 std::string。
第 6 行删除了 c-string (HEAP CORRUPTION HAPPENS HERE)
第 7 行返回了字符串,但由于出错,它从未到达这一点。

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

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

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

有任何想法吗?

4

6 回答 6

8

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

于 2010-11-18T21:09:47.483 回答
4

改变:

char* rawString = new char[strLen];

到:

char* rawString = new char[strLen + 1];
于 2010-11-18T21:09:57.247 回答
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);
于 2010-11-18T21:09:28.713 回答
1

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

于 2010-11-18T21:10:36.130 回答
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;
}

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

于 2010-11-19T07:42:07.480 回答
0
 rawString[strLen] = '\0';

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

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

于 2010-11-18T21:11:16.840 回答