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

使用struct读写二进制文件:嵌套结构体与数组加载问题

嵌套结构体的二进制文件读写(解决读取访问违例问题)

我来帮你搞定这个问题——你遇到的读取访问违例,大概率是因为直接写入/读取了结构体里的指针成员。指针存的是当前程序的内存地址,等你下次读取的时候这个地址早就无效了,自然会触发非法内存访问。下面我用C++代码为例,一步步讲正确的读写方式,包括如何恢复A*数组和嵌套的B结构体。

首先先明确安全的结构体定义:如果结构体里有动态数据,别直接用指针存,要搭配长度字段,而且嵌套的结构体尽量用值类型成员(比如固定大小数组,别用char*这种指针):

#include <fstream>
#include <iostream>
#include <cstring>

// 强制1字节对齐,避免跨平台编译的内存对齐差异
#pragma pack(push, 1)
struct B {
    int id;
    char name[50]; // 用固定大小数组存字符串,别用char*
};

struct A {
    int b_count;   // 记录当前A包含的B结构体个数
    B* b_list;     // 动态分配的B数组指针
};
#pragma pack(pop)

一、写入二进制文件的正确实现

不能直接把整个A结构体丢进文件(因为里面的b_list是指针,存的是内存地址),得按「元数据+实际数据」的顺序写:先写A数组的总个数,再逐个写每个A的元数据(b_count),最后写每个B的实际内容。

void writeToBinary(const char* filename, const A* a_array, int a_count) {
    std::ofstream outfile(filename, std::ios::binary | std::ios::out);
    if (!outfile.is_open()) {
        std::cerr << "Failed to open file for writing!\n";
        return;
    }

    // 第一步:写入A数组的总长度,读取时知道要分配多少个A
    outfile.write(reinterpret_cast<const char*>(&a_count), sizeof(a_count));

    // 逐个处理每个A对象
    for (int i = 0; i < a_count; ++i) {
        const A& current_a = a_array[i];
        // 写入当前A包含的B数组长度
        outfile.write(reinterpret_cast<const char*>(&current_a.b_count), sizeof(current_a.b_count));

        // 写入每个B结构体的完整内容(因为B是值类型,内存连续可直接写)
        for (int j = 0; j < current_a.b_count; ++j) {
            outfile.write(reinterpret_cast<const char*>(&current_a.b_list[j]), sizeof(B));
        }
    }

    outfile.close();
    std::cout << "Data written to file successfully!\n";
}

二、读取二进制文件并恢复结构体

读取必须严格遵循写入的顺序:先读A数组的总个数,再逐个创建A对象,给每个A分配B数组的内存,最后把文件里的B数据读进数组。

A* readFromBinary(const char* filename, int& out_a_count) {
    std::ifstream infile(filename, std::ios::binary | std::ios::in);
    if (!infile.is_open()) {
        std::cerr << "Failed to open file for reading!\n";
        out_a_count = 0;
        return nullptr;
    }

    // 第一步:读取A数组的总长度
    infile.read(reinterpret_cast<char*>(&out_a_count), sizeof(out_a_count));

    // 分配A数组的内存
    A* a_array = new (std::nothrow) A[out_a_count];
    if (!a_array) {
        std::cerr << "Failed to allocate memory for A array!\n";
        out_a_count = 0;
        infile.close();
        return nullptr;
    }

    // 逐个恢复每个A对象
    for (int i = 0; i < out_a_count; ++i) {
        A& current_a = a_array[i];
        // 读取当前A包含的B数组长度
        infile.read(reinterpret_cast<char*>(&current_a.b_count), sizeof(current_a.b_count));

        // 分配B数组的内存
        current_a.b_list = new (std::nothrow) B[current_a.b_count];
        if (!current_a.b_list) {
            std::cerr << "Failed to allocate memory for B array!\n";
            // 内存分配失败,要释放之前分配的所有内存避免泄漏
            for (int k = 0; k < i; ++k) {
                delete[] a_array[k].b_list;
            }
            delete[] a_array;
            out_a_count = 0;
            infile.close();
            return nullptr;
        }

        // 读取每个B的内容到数组里
        for (int j = 0; j < current_a.b_count; ++j) {
            infile.read(reinterpret_cast<char*>(&current_a.b_list[j]), sizeof(B));
        }
    }

    infile.close();
    std::cout << "Data read from file successfully!\n";
    return a_array;
}

三、验证读取结果的打印函数

可以用这个函数打印恢复后的A数组内容,确认读写是否正确:

void printAArray(const A* a_array, int a_count) {
    for (int i = 0; i < a_count; ++i) {
        std::cout << "\n=== A[" << i << "] ===";
        std::cout << "\nNumber of B objects: " << a_array[i].b_count;
        for (int j = 0; j < a_array[i].b_count; ++j) {
            const B& b = a_array[i].b_list[j];
            std::cout << "\n  B[" << j << "]: ID=" << b.id << ", Name=" << b.name;
        }
    }
    std::cout << "\n";
}

四、避坑指南(重点!)

  • 绝对不要直接写带指针的结构体:指针存的是内存地址,不是实际数据,读取时这个地址无效,必然触发访问违例。一定要把指针指向的实际数据写入文件,同时记录数据长度。
  • 处理内存对齐:不同编译器的结构体对齐规则不同,跨平台读写时必须用#pragma pack强制对齐,否则读写的字节数会不一致,导致数据错乱。
  • 记得释放内存:使用完恢复后的A数组和B数组,一定要手动释放内存,避免内存泄漏:
    void freeAArray(A* a_array, int a_count) {
        for (int i = 0; i < a_count; ++i) {
            delete[] a_array[i].b_list;
        }
        delete[] a_array;
    }
    
  • 检查文件操作状态:每次读写后都要确认流的状态,避免因为文件损坏、磁盘空间不足等问题导致读写不完整,进而触发后续的内存错误。

内容的提问来源于stack exchange,提问作者Master-Antonio

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 08:20:29