这是我的代码:
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。
有什么想法吗?
答案 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