3

以下のコードのデバッグを手伝ってください。私がやっていることはvector<string>、バイナリファイルにシリアル化してそこから取得することです。これがメインコードの例です。

    /*  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」から直接ベクトルを取得しようとすると、forループの直前にサイズが4として正しく出力されますが、セグメンテーション違反イベントが表示されます。 。これは、これ(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、最後のforループのステートメントです。誰かが私がこれをデバッグするのを手伝ってもらえますか?また、上記のコードが移植可能かどうかも教えてください。

ありがとう、プラブ

4

4 に答える 4

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);
}
于 2012-11-15T11:49:08.450 に答える
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);
}
于 2012-11-15T11:49:00.633 に答える
1

C / C ++では、実装を理解していて、その中にポインターがない場合を除いて、シリアル化のために構造体またはクラスを保存しないでください。
より良い方法は、ブーストシリアル化を使用することです。彼らはすでに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
    }
}
于 2012-11-15T13:03:36.007 に答える
0

sizeof( std::string )オブジェクトのサイズを示しstringます。実際の文字列データ自体は動的であり、stringクラス内のポインタによって保持されます。

オブジェクトのシリアル化/逆シリアル化には、 Googleプロトコルバッファを使用するか、シリアル化をブーストすることをお勧めします。

于 2012-11-15T11:52:15.347 に答える