2015-10-07 64 views
1

爲什麼在插入向量後內存已損壞。在下面的程序中,我有一個字符串var的結構(它不是一個指針)。我每次創建一個本地結構對象並分配一個字符串值並將其推送到該向量。在推送到向量後,我正在對本地結構對象進行更改。但是這種變化正在反映在向量結構對象的字符串數據中。推入載體後內存已損壞

#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 ==>內存損壞?爲什麼沒有blabla字符串?
bool value:1
id:10
線程名稱:Wrongd2 ==>內存損壞?爲什麼沒有blabla字符串?
布爾值:1個

+1

你說「即使在」之後,彷彿進入一個向量魔法般地修復所有內存腐敗。 –

+1

這樣跺腳在琴絃記憶中只會導致眼淚 – paulm

回答

2

TL;在const指針(這是不確定的行爲)博士

memcpy()相沖突的寫入時複製優化運行。


是,vector::push_back()推動對象的副本到載體中。所以當你push_back()編輯你的本地對象時,對局部對象的改變不應該影響向量中的對象,對嗎?

然而,std::string允許假設任何對它的訪問將以明確定義的方式發生。對.c_str()返回的(const)指針做memcpy()的定義不明確。

所以......讓我們說,std::string複印時載體中的thread_info對象了一條捷徑:而不是複製所包含的數據,它複製了指針的數據,這樣兩個std::string對象引用相同的內存區。

它可以將複製推遲到實際上變得必要時(即,當通過任何定義的函數(如string::insert()operator+=)寫入其中一個字符串時)。這被稱爲「寫入時複製」,這是一種相當常見的優化。

通過從.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對象是意識到出現了寫訪問,所以數據沒有實際的複製已經發生。

5

memcpy ing的結果是錯誤。難道你不得不用暗示破解const這個事實嗎?哪個學習資源教你做到這一點?

由於paulm所以說得好:

踏着字符串的內存一樣,只會導致眼淚。

std::string::c_str()返回一個指向你不應該修改的常量緩衝區的指針;由於某些工具鏈中存在某些優化(例如GCC < 5.0中的SSO),它甚至可能不是字符串真正的底層緩衝區,這在您看來就是這種情況。

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