2012-11-15 97 views
3

請幫我調試下面的代碼。我所做的只是將vector<string>序列化爲二進制文件並從中取回。這裏是示例主代碼,向量序列化/反序列化中的C++分段錯誤

/* Portion Commented */ 
vector<string> list; 

list.push_back("AAAAAA"); 
list.push_back("BBBBBB"); 
list.push_back("CCCCCC"); 
list.push_back("DDDDDD"); 

// Write out a list to a disk file 
ofstream os ("/home/test/data.dat", ios::binary); 

int size1 = list.size(); 
os.write((const char*)&size1, sizeof(int)); 
os.write((const char*)&list[0], size1 * sizeof(string)); 
os.close(); 
/* Portion Commented */ 

// Read it back in 
VertexList list2; 

ifstream is("/home/test/data.dat", ios::binary); 
int size2; 
is.read((char*)&size2, sizeof(int)); 

list2.resize(size2); 
cout<<"Size is :"<<size2<<endl; 
is.read((char*)&list2[0], size2 * sizeof(string)); 
for (int i=0; i < size2; i++) 
{ 
     cout<<"At i = "<<i<<", "<<list2[i]<<endl; //Line 40 in my program 
} 

我有4個元素推入向量列表。然後,我序列化矢量並將其寫入一個二進制文件並從中取回。它工作正常。

後來當我在上面的代碼中評論'Portion Commented'並試圖直接從已創建的二進制文件「data.data」中檢索矢量時,它顯示了段錯誤事件,雖然它正確地將尺寸打印爲4在for循環之前。這是這個(valgrind --leak-check=yes ./a.out)創建了Valgrind的輸出,

==14058== Invalid read of size 8 
==14058== at 0x4EBE263: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /usr/lib/libstdc++.so.6.0.14) 
==14058== by 0x40107F: main (test2.cpp:40) 
==14058== Address 0x2156010 is not stack'd, malloc'd or (recently) free'd 

40號線是在最後for循環cout聲明。有人可以幫我調試嗎?另外告訴我,上面的代碼是否可移植?

感謝, Prabu

+3

'sizeof(string)'不會做你認爲它的作用。 – Vikas

+0

你忘了實際*序列化*任何東西。您只是將內存內容寫入磁盤,而不用擔心是否可以重新解釋它們以恢復數據的*含義*。 –

回答

3

std::string實現包括一個指向實際字符串內容堆。因此,sizeof(string)只是指針加上一些字節。如果你想寫字符串,你必須寫內容本身

for (auto i = list.begin(); i != list.end(); ++i) { 
    os.write(i->c_str(), i->size() + 1); 
} 

當你讀回來,你必須尋找終止NUL字節。另外,您也可以保存字符串的長度,因爲你已經與列表

for (auto i = list.begin(); i != list.end(); ++i) { 
    int len = i->size() + 1; 
    os.write((const char*)&len, sizeof(len)); 
    os.write(i->c_str(), i->size() + 1); 
} 
+0

感謝您的意見。但有一個問題,它在第一種情況下的運作正如我所期望的那樣(即取消註釋評論部分)。在這種情況下,for循環會精確地打印內容。而當我試圖直接從data.dat文件中檢索時,它在for循環中崩潰。這是什麼原因? – Prabu

+1

^關鍵是你已經序列化列表而不是序列化列表的內容。 – Prabhu

+0

@Prabhu很好看! –

2
os.write((const char*)&list[0], size1 * sizeof(string)); 

你在這裏幹什麼?將std::string投射到const char*?這沒有意義。

如果你使用C++風格的轉換,編譯器會告訴你爲什麼它沒有意義。這就是爲什麼C++程序員應該避免使用C風格的演員!

你可能想要做的是這樣的:

os.write(list[0].c_str(), list[0].size() + 1); 

而且你應該做的是在一個循環爲:

for(auto const & s : list) //s is inferred to be std::string 
{ 
    os.write(s.c_str(), s.size() + 1); 
} 
+0

總是我在使用C風格和C++風格方面犯了錯誤。感謝您指出它。 – Prabu

0

sizeof(std::string)給你string物體的大小來完成。實際的字符串數據本身是動態的,並由string類中的指針保存。

您可能想要使用google protocol bufferboost serialize來序列化/反序列化對象。

1

在C/C++中,除非您知道實現並且沒有指針,否則不應保存用於序列化的結構或類。
更好的方法是使用boost序列化。他們已經盡一切努力來支持序列化/反序列化STL對象。

#include <boost/archive/binary_oarchive.hpp> 
#include <boost/archive/binary_iarchive.hpp> 
#include <boost/serialization/string.hpp> 
#include <boost/serialization/vector.hpp> 
#include <iostream> 
#include <vector> 
#include <fstream> 
#include <string> 
using namespace std; 
int main(int ac, char **av) 
{ 
    vector<string> list1; 

    list1.push_back("AAAAAA"); 
    list1.push_back("BBBBBB"); 
    list1.push_back("CCCCCC"); 
    list1.push_back("DDDDDD"); 

    // Write out a list to a disk file 
    ofstream os ("data.dat", ios::binary); 

    boost::archive::binary_oarchive oa(os); 
    oa << list1; 
    os.close(); 

    vector<string> list2; 

    ifstream is("data.dat", ios::binary); 
    boost::archive::binary_iarchive ia(is); 
    ia >> list2; 
    int size2 = list2.size(); 
    for (int i=0; i < size2; i++) 
    { 
     cout<<"At i = "<<i<<", "<<list2[i]<<endl; //Line 40 in my program 
    } 
} 
+0

感謝您的評論。我知道這個選項在提升。但是到目前爲止,我們的產品還沒有使用這個Boost庫。所以只有我試圖避免相同。任何未來,我們都會嘗試將其整合到我們的產品中。 – Prabu