内存被推入向量后损坏

为什么内存被推入向量后被损坏。 在下面的程序中,我有一个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

Solutions Collecting From Web of "内存被推入向量后损坏"

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