为什么内存被推入向量后被损坏。 在下面的程序中,我有一个stringvar的结构(它不是一个指针)。 我每次创build一个本地结构对象,并分配一个string值并推送到向量。 推到vector后,我正在对本地结构对象进行更改。 但是这种变化正在反映在向量结构对象的string数据中。
#include <iostream> #include <vector> #include <string> #include <memory.h> using namespace std; void PushVector(string); struct thread_info { int id; string threadname; bool bval; }; std::vector<thread_info> myvector; int main () { PushVector("Thread1"); // valid data into vector PushVector("Thread2"); struct thread_info print; while(!myvector.empty()) { for(unsigned int index = 0; index < myvector.size(); ++index ) { print = myvector.at(index); cout<<"id : "<<print.id<<"\nthread name : "<<print.threadname<<"\nbool value : "<<print.bval<<endl; } myvector.clear(); } return 0; } void PushVector(const string str) { std::cout << "Push the thread name to vector\n"; struct thread_info thread; thread.id = 10; thread.threadname = str; thread.bval = true; myvector.push_back (thread); //copying struct obj to vector char* p = (char* )thread.threadname.c_str(); memcpy(p,"Wrong", 5); //==> Memory corrupting with invalid data after push back. Is it a limitation in C++? thread.threadname = "blabla"; //trying to corrupt directly to string object }
o / p:将线程名称推向向量
将线程名称推向向量
ID:10
线程名称:Wrongd1 ==>内存损坏? 为什么没有blablastring?
布尔价值:1
ID:10
线程名称:Wrongd2 ==>内存损坏? 为什么没有blablastring?
布尔价值:1
TL;博士
您的memcpy()
在一个const
指针(这是未定义的行为)违反了写时复制优化。
是的, vector::push_back()
将对象的一个副本放入向量中。 所以,当你push_back()
编辑你的本地thread_info
对象后,对本地对象的改变不应该影响向量中的对象,对吧?
但是, std::string
可以假定对它的任何访问都会以一个明确的方式进行。 对.c_str()
返回的( const
)指针做一个memcpy()
没有很好的定义。
所以…让我们说std::string
在将thread_info
对象复制到vector中时采用了一个快捷方式:它不是复制包含的数据,而是将指针复制到数据,以便两个std::string
对象引用相同的内存区。
它可以推迟复制到什么时候(和如果)实际上是必要的,即当其中一个字符串通过任何定义的函数(如string::insert()
或operator+=
)写入。 这被称为“写入时拷贝”,这是一个相当普遍的优化。
通过从.c_str()
的返回值中.c_str()
const
并在其上运行一个memcpy()
,你挫败了这个机制。 由于您没有经过任何可以完成写入时拷贝的string
成员函数,所以这两个对象(应该是不同的)仍然指向相同的数据存储器。
GDB输出,断点在PushVector()
的最后一行:
(gdb) print &thread $3 = (thread_info *) 0x7fffffffe240 (gdb) print &myvector[0] $4 = (thread_info *) 0x605040
两个thread_info
对象是不同的。
(gdb) print &thread.threadname $5 = (std::string *) 0x7fffffffe248 (gdb) print &myvector[0].threadname $6 = (std::string *) 0x605048
两个string
对象也是不同的。
(gdb) print thread.threadname.c_str() $7 = 0x605028 "Wrongd1" (gdb) print myvector[0].threadname.c_str() $8 = 0x605028 "Wrongd1"
但是它们指向相同的内存区域,因为这两个string
对象都不知道已经有写入权限,所以没有发生实际的数据复制。
memcpy
到.c_str()
的结果是错误的 。 事实是你不得不用暗示const
而不是暗示? 哪个学习资源教你做到这一点?
正如保姆那般恰如其分地说:
跺脚像这样的字符串的记忆只会导致眼泪。
std::string::c_str()
返回一个指向你不能修改的常量缓冲区的指针; 由于在某些工具链中存在某些优化(例如,GCC中的SSO <5.0),它甚至可能不是字符串真正的底层缓冲区,这在你看来就是这种情况。
忘记memcpy
; 这不是C.
充其量 ,你可以做到这一点:
thread.threadname.resize(5); memcpy(&thread.threadname[0], "Wrong", 5);
或者,在C ++代码中:
thread.threadname.resize(5); std::copy("Wrong", "Wrong"+5, &thread.threadname[0]);
但是,对于实际情况,你应该写:
thread.threadname = "Wrong";