简体   繁体   中英

C++ Segmentation Fault in vector serialize/deserialize

Please help me debug the below code. What I am doing is just serialize vector<string> into binary file and retrieving it back from it. Here is the example main code,

    /*  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
}

I have 4 elements pushed into vector list. Then I serialize the vector and write it in a binary file and retrieving it back from same. It works fine.

Later when I comment the 'Portion Commented' in the above code and tried to retrieve the vector directly from the already created binary file "data.data", it shows segmentation fault event though it prints the size correctly as 4 just before the for loop. This is my valgrind output created with this ( valgrind --leak-check=yes ./a.out ),

==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 

Line 40 is the cout statement in the last for loop. Can someone help me debug this ? Also tell me whether the above code is portable or not ?

thanks, Prabu

The implementation of std::string includes a pointer to the actual string contents on the heap. So, sizeof(string) is just the pointer plus some bytes. If you want to write the string, you must write the contents itself

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

When you read that back in, you must look for the terminating NUL byte. Alternatively, you can save the length of the string, as you have done with the list

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);
}
os.write((const char*)&list[0], size1 * sizeof(string));

What are you doing here? Casting std::string into const char* ? That doesn't make sense.

If you use C++-style cast, the compiler will tell you why it doesn't make sense. That is why C++ programmers should avoid using C-style cast!

What you probably want to do is this:

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

And you should do that in a loop as:

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

In C/C++, you should not save the structure or class for serialization, unless you know the implementation and there are no pointers in that.
The better way is to use boost serialization. They already did everything to support serialize/deserialize STL objects.

#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
    }
}

sizeof( std::string ) gives you size of the string object. Actual string data itself is dynamic and held by a pointer in string class.

You might want to use google protocol buffer or boost serialize for serialize/de-serialize objects.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM