C++ 访问冲突错误

C++ Access Violation Error

我写了一个简单的程序来学习如何使用随机访问填充。它可以正常编译,但在运行时会出现访问冲突错误。我只写和读一条记录。

头文件:

#include<iostream>
#include<string>
#include<fstream>
using namespace std;

#ifndef HEADER_H
#define HEADER_H


class info
{
private:
    int id;
    string name;
public:
    info(int = 0, string = " ");
    void set(int, string);
    void display();
    void write();
    void read();
};

#endif

实现文件:

#include<iostream>
#include<string>
#include<fstream>
#include"Header.h"
using namespace std;

info::info(int x, string y)
{
    set(x, y);
}

void info::set(int x, string y)
{
    id = x;
    name = y;
}

void info::display()
{
    cout << "\n\n\tid : " << id;
    cout << "\n\tname" << name;
}

void info::write()
{
    ofstream o("info.dat", ios::out | ios::binary | ios::app);
    info a(id, name);
    info *p = &a;
    o.write(reinterpret_cast<const char *>(p), sizeof(info));
    o.close();
    cout << "\n\n\tWrite Successful";
}

void info::read()
{
    ifstream i("info.dat", ios::in | ios::binary);
    i.seekg(0);
    info a(0, "a");
    info *p = &a;
    i.read(reinterpret_cast<char *>(p), sizeof(info));
    i.close();
    p->display();
    cout << "\n\n\tRead Successful";
}

主线:

#include<iostream>
#include<string>
#include<fstream>
#include"Header.h"
using namespace std;

void main()
{
    info a(10, "muaaz");
    a.write();
    a.display();
    info b(2, "m");
    b.read();
}

读取函数后出现错误。 read 函数末尾的 cout "Read Successful" 运行正常,并且在 main.cout 之后没有其他语句。我不知道是什么导致了错误。

您的代码中每次出现 reinterpret_cast 都应该强烈提示您正在发生危险且容易出错的事情。

void info::write()
{
    ofstream o("info.dat", ios::out | ios::binary | ios::app);
    info a(id, name);
    info *p = &a;
    o.write(reinterpret_cast<const char *>(p), sizeof(info));
    o.close();
    cout << "\n\n\tWrite Successful";
}

这无法正常工作。撇开所有语言律师问题不谈,请考虑此处的实施观点。 std::string 通常不直接包含(作为数据成员)它表示的文本,而是包含一个 指针 指向存储文本的动态分配内存。

所以你最终要做的是将内存地址写入文件。

对于任何序列化业务来说,这已经足以证明该方法注定要失败,因为很明显,同一程序的不同运行的内存地址是不同的。

但是即使在这个简单的示例中,当在同一执行中写入和读取文件时,它也不会起作用。 a 是本地对象。当 write 完成时,对象被销毁,它包含的 std::string 也随之被销毁。当 std::string 被销毁时,它为其内容分配的内存将被释放。

因此,一旦 write 完成,您的文件就会包含一个无效的内存地址。片刻之后,在 read 函数中,您尝试将无效地址填充到新的 std::string 中。幸运的是,您遇到了访问冲突,因此您注意到出了点问题。如果你不走运,程序会继续产生所需的行为一段时间,只是在稍后开始崩溃或做奇怪的事情。


使情况更加复杂的一些问题:

  • C++ 标准未指定 std::string 的内存布局。因此,即使您从完全相同的 C++ 源代码编译它们,具有不同编译器设置的不同编译器也会生成不同的 info.dat 文件。

  • std::string 可能使用也可能不使用小字符串优化 (SSO),这种技术意味着对于只有几个字符的字符串,文本 直接存储在对象中,而不是动态分配。所以你甚至不能假设指针的存在。

  • 另一方面,std::string甚至可能包含 更多 指向动态分配内存的指针,指向所表示文本的末尾和分配内存的末尾(对于 capacity 成员函数)。


考虑到所有这些重要的内部复杂性,当您试图通过简单的 reinterpret_cast 绕过或忽略所有这些复杂性时会调用 未定义的行为 ,您应该不会感到惊讶。

非 POD 类型的序列化,尤其是反序列化在 C++[*] 中并不容易,并且尚未标准化。您可以考虑第三方库。与往常一样,Boost 在第三方库方面值得一试,事实证明,它确实包含一个序列化库,即 Boost.Serialization.


[*] 实际上,这在任何语言中都不容易,即使在那些假装很容易的语言中也不容易,例如 Java 及其高度危险的 Serializable界面.