GCC下的对象加载段错误

Object loading segfault under GCC

本文关键字:错误 段错误 加载 对象 GCC      更新时间:2023-10-16

这些方法应该保存和加载与它们相关联的整个对象。当我通过gcc在Linux下编译程序时,保存似乎可以工作,但在加载时它会出现分段错误。当我在Windows下通过Visual Studio编译器编译它时,它就像梦一样工作。我不确定有什么不同,但我有一种预感,它涉及到一些奇怪的gcc。

两种方法:

void User::SaveToFile()
{
  ofstream outFile;
  string datafile_name = username + "_data";
  outFile.open(datafile_name.c_str(), ios::binary);
  outFile.write((char*)this, sizeof(*this));
}
void User::LoadFromFile(string filename)
{
  ifstream inFile;
  inFile.open(filename.c_str(), ios::binary);
  inFile.read((char*)this, sizeof(*this));
}

声明:

class User
{
private:
  string username;
  string realname;
  string password;
  string hint;
  double gpa;
  vector<Course> courses;
public:
  double PredictGPA();
  void ChangePassword();
  void SaveToFile();
  void LoadFromFile(string filename);
  void SetUsername(string _username){username = _username;}
  string GetUsername(){return username;}
  void SetRealname(string _realname){realname = _realname;}
  string GetRealname(){return realname;}
  void SetPass(string _password){password = _password;}
  string GetPass(){return password;}
  void SetHint(string _hint){hint = _hint;}
  string GetHint(){return hint;}
};

您的class User不是POD类型,它不是普通的旧数据类型(如C结构体)。你不能只是按位读写它的内存并期望它能工作。stringvector都不是pod,它们保留指向动态分配数据的指针。当读取这些回读时,试图访问无效内存将导致段错误。更重要的是,stringvector的内容实际上根本没有被保存,因为它们不在对象的内存布局中(它有时可能与string和SBO一起工作,但它只是偶然的,仍然没有定义这样做)。

你需要一种方法来序列化和反序列化你的类;当你像那样读入类时,你的类不可能神奇地变成一个对象。

相反,你需要提供你在加载/保存类时调用的函数,这些函数以你选择的某种格式存储类,例如XML。

所以不用

outFile.write((char*)this, sizeof(*this));

有一些成员函数将其转换为具有加载时可以轻松解析的格式的字符串(或您认为更容易的二进制格式),然后保存它。

outFile.write(this->myserialize(), mysize);

你不能那样写进string。一方面,它通常动态地存储数据,也就是说,它根本不在对象内部;另一方面,你不必依赖于它的任何特定布局。

向量也有类似的问题,你似乎根本没有考虑到端序和填充。

简单地说,你在做不成立的假设。

一般来说,不要在字节级别上混淆复杂(非pod)对象。用某种文本格式序列化,使用对象的公共成员函数来提取和恢复它们的状态。

你考虑过JSON吗?

字符串等可能包含指针——在这种情况下,你的方法可能会出现严重的错误。

您需要序列化数据-即将其转换为一系列字节。

当读取数据时,您只需读取字节,然后从中创建对象。新的指针将是正确的

如果你坚持这个路线,我会写字符串的长度,而不是null终止它。在装载时更容易分配。在二进制格式中有很多需要考虑的问题。每个字段都应该有某种类型的ID,以便在错误的位置或不同版本的程序中可以找到它。此外,在你的文件开始写你正在使用的尾序和你的整数的大小等。或者为所有东西确定一个标准的大小和顺序。我以前总是这样编写网络和文件存储的代码。有更好的现代方法。还可以考虑使用缓冲区并创建Serialize()函数。

好的现代替代品包括:SQLite3, XML, JSON

未经测试的例子:

class object
{
Load()
{
  ifstream inFile;
  int size;
  inFile.open("filename", ios::binary);
  inFile.read(&size, 4);  
  stringA.resize(size);
  inFile.read(&stringA[0], size);
  inFile.read(&size, 4);  
  stringB.resize(size);
  inFile.read(&stringB[0], size);
  inFile.close();          //don't forget to close your files
}
Save()
{
  ofstream outFile;
  int size;
  outFile.open("filename", ios::binary);
  size = stringA.size();   
  outFile.write(&size, 4);
  outFile.write(&stringA[0], size);
  size = stringB.size();       
  outFile.write(&size, 4);
  outFile.write(&stringA[0], size);
  outFile.close();
}
private:
std::string stringA
std::string stringB
};