推入向量后,内存已损坏

时间:2015-10-07 09:22:08

标签: c++ linux stl

为什么在推入向量后内存被破坏了。在下面的程序中,我有一个带字符串var的结构(它不是指针)。我每次创建一个本地struct对象并分配一个字符串值并推送到向量。在推送到向量后,我正在对本地struct对象进行更改。但是这种变化正在向量结构对象的字符串数据中反映出来。

    #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 ==&gt;内存已损坏?为什么没有blabla字符串?
      bool值:1
      id:10
      线程名称:Wrongd2 ==&gt;内存已损坏?为什么没有blabla字符串?
      bool值:1

2 个答案:

答案 0 :(得分:5)

memcpy .c_str()的结果是错误。事实上你必须用一个演员而不是暗示来攻击const吗?哪种学习资源教会你这样做?

正如paulm所说:

  

像这样踩到弦乐的记忆只会导致眼泪。

std::string::c_str()返回一个指向您不应修改的常量缓冲区的指针;由于某些工具链中存在某些优化(例如GCC中的SSO&lt; 5.0),它甚至可能不是真正的底层缓冲区,这对你来说就好了。

忘记memcpy;这不是C。

best ,您可以这样做:

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

答案 1 :(得分:2)

<强> TL;博士

memcpy()指针上的const(未定义的行为)与写时复制优化相冲突。

是的,vector::push_back()将对象的副本推送到向量中。因此,在push_back()编辑本地thread_info对象后,对本地对象的更改不应该影响向量中的对象,对吗?

但是,std::string 允许假设对其的任何访问都将以明确定义的方式发生。对memcpy()返回的{const)指针执行.c_str()定义不明确。

所以......让我们说std::string在将thread_info对象复制到向量时采用了一种捷径:它不是复制包含的数据,而是复制了指针数据,以便两个std::string对象引用相同的内存区域。

它可以将复制推迟到实际变得必要的时间(如果),即当通过任何定义的函数(如string::insert()operator+=)写入其中一个字符串时。这称为“写时复制”,这是一种相当常见的优化。

通过从const的返回值中删除.c_str()并在其上运行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对象都没有意识到存在写访问权限,因此没有实际复制数据。