You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C++程序首次运行正常,第二次读取已有文件时崩溃如何解决

崩溃根因

这是C二进制序列化的典型错误写法,首次运行不崩溃纯属运气,二次启动崩溃是必然结果,核心问题是你直接读写非POD类型的原始内存,完全违反C对象模型规则:

  • 直接读写std::vector的内存:std::vector本身只存三个指向堆内存的指针(起始地址、已用末尾、容量末尾),实际的学生元素存在堆上。你写文件时只存了这三个指针的数值,根本没有保存真实的学生数据。首次运行写完立刻读取,指针指向的堆内存还没释放,所以看起来能正常运行;二次启动时读回来的是上一次进程的指针地址,在当前进程中属于非法地址,访问直接触发段错误。
  • 直接读写包含std::string的TStudent对象内存:std::string和vector逻辑类似,内部同样存指向堆内存的指针,dump对象内存只会保存指针值,不会保存实际的字符串内容,读回的指针同样是非法地址。
  • 保存文件误用ios::app追加模式:首次运行已经写入了一组无效的指针数据,二次启动会再追加一组新的指针数据,读取时只会读到最早的无效指针,进一步放大问题。
修复方案

你需要自行实现合法的序列化逻辑,写入实际的业务数据而非对象内存地址,核心修改点如下:

  1. 改写salveaza保存逻辑:
    • 废弃ios::app模式,根据需求选ios::trunc覆盖写入全量数据
    • 先写入当前学生总数,再逐个遍历学生,依次写入名字、姓氏、年龄的实际内容
  2. 改写incarca加载逻辑:
    • 先读取学生总数,再循环对应次数,每次读取名字、姓氏、年龄后构造TStudent对象,添加到vector中

修改后的核心代码参考:

// 序列化写入
void salveaza(string file_name)
{
    ofstream file1(file_name, ios::trunc | ios::binary);
    if (!file1) {
        cout << "文件打开失败" << endl;
        return;
    }
    // 先写元素总数
    size_t count = students.size();
    file1.write((char*)&count, sizeof(count));
    for (const auto& s : students) {
        // 写字符串长度 + 实际内容
        size_t name_len = s.Name.size();
        file1.write((char*)&name_len, sizeof(name_len));
        file1.write(s.Name.data(), name_len);
        size_t surname_len = s.Surname.size();
        file1.write((char*)&surname_len, sizeof(surname_len));
        file1.write(s.Surname.data(), surname_len);
        // 写年龄
        file1.write((char*)&s.Age, sizeof(s.Age));
    }
    file1.close();
    cout << "\n文件保存成功.\n" << endl;
}

// 反序列化读取
void incarca(string file_name)
{
    ifstream file2(file_name, ios::binary);
    if(!file2)
    {
        cout << "文件打开失败.";
        return;
    }
    students.clear();
    // 先读元素总数
    size_t count = 0;
    file2.read((char*)&count, sizeof(count));
    for (size_t i = 0; i < count; i++) {
        // 读名字
        size_t name_len = 0;
        file2.read((char*)&name_len, sizeof(name_len));
        string name(name_len, '\0');
        file2.read(&name[0], name_len);
        // 读姓氏
        size_t surname_len = 0;
        file2.read((char*)&surname_len, sizeof(surname_len));
        string surname(surname_len, '\0');
        file2.read(&surname[0], surname_len);
        // 读年龄
        int age = 0;
        file2.read((char*)&age, sizeof(age));
        // 构造对象添加到列表
        students.emplace_back(name, surname, age);
    }
    cout << "文件加载成功.\n" << endl;
}

另外补充:TStudent里的aux1、aux2字段完全无用,sizeof(std::string)是固定的(等于内部指针的大小)和实际存储的字符串长度无关,可以直接删除。

内容的提问来源于stack exchange,提问作者Vlad

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.03 18:57:01