2012-11-15 6 views
3

아래 코드를 디버깅하는 데 도움주세요. 내가 뭘하는지 vector<string> 이진 파일로 직렬화하고 그것을 다시 검색 할 수 있습니다. 다음은 예제 메인 코드입니다.벡터에서 C++ 세그먼트 화 오류 serialize/deserialize

/* 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 가지 요소가 벡터 목록에 푸시되었습니다. 그런 다음 벡터를 직렬화하여 이진 파일에 쓰고 같은 파일에서 다시 검색합니다. 그것은 잘 작동합니다.

나중에 위의 코드에서 '주석 부분'에 주석을 달고 이미 만든 이진 파일 "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)은 루프의 마지막에서 cout 문입니다. 누군가가 이것을 디버깅 할 수 있습니까? 또한 위의 코드가 이식 가능한지 여부를 알려주시겠습니까?

덕분에, Prabu

+3

'sizeof (string)'은 (는) 생각한대로 작동하지 않습니다. – Vikas

+0

실제로 * serialize하는 것을 잊어 버렸습니다.방금 데이터의 의미 *를 복구하기 위해 다시 해석 할 수 있는지에 대한 염려없이 디스크의 메모리 내용을 디스크에 썼습니다. –

답변

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::stringconst 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 클래스의 포인터로 유지됩니다.

개체의 serialize/de-serialize에 google protocol buffer 또는 boost serialize을 사용할 수 있습니다.

1

C/C++에서 구현을 알고 포인터가없는 경우 C/C++에서는 직렬화를 위해 구조 또는 클래스를 저장하면 안됩니다.
더 좋은 방법은 부스트 ​​직렬화를 사용하는 것입니다. 그들은 이미 STL 객체를 serialize/deserialize 할 수 있도록 모든 것을했습니다.

#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 Library를 사용하지 않았습니다. 그래서 나는 단지 같은 것을 피하려고 노력했다. 앞으로 어떻게 될지, 우리는이를 제품에 통합하려고 노력할 것입니다. – Prabu