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

C语言实现含堆指针的结构体数组与缓冲区的序列化和反序列化方法

C语言含堆指针结构体数组的序列化与反序列化实现

核心痛点是结构体内部的ptr是堆内存指针,直接拷贝结构体只会保存指针的虚拟地址,反序列化后该地址完全无效,因此必须将ptr指向的实际数据也写入序列化缓冲区,重建时再重新分配堆内存拷贝数据。

实现思路

序列化步骤

  • 第一步:计算总缓冲区大小 = 存储数组长度的int字节 + 所有struct Foo普通成员的大小之和 + 所有Foo::ptr指向的内存大小之和
  • 第二步:申请对应大小的堆缓冲区
  • 第三步:先写入数组总长度到缓冲区头部
  • 第四步:遍历每个结构体元素,依次写入结构体的普通成员(a、b、size_of_ptr、d),紧接着写入ptr指向的实际数据,不断偏移缓冲区指针直到所有元素处理完成

反序列化步骤

  • 第一步:从缓冲区头部读取数组总长度n_objs
  • 第二步:申请sizeof(struct Foo) * n_objs的内存作为新的结构体数组
  • 第三步:遍历每个元素,先读取普通成员赋值给新结构体,再根据读到的size_of_ptr申请对应大小的堆内存给新结构体的ptr,最后把缓冲区中存储的ptr数据拷贝到新申请的堆内存中
  • 第四步:返回重建完成的结构体数组指针

完整代码示例

#include <stdlib.h>
#include <string.h>

struct Foo {
    int a;
    int b;
    void* ptr; // 指向堆分配内存
    int size_of_ptr; 
    double d;
};

// 序列化函数,返回堆分配的缓冲区,使用完后需要free
void* serialize(struct Foo* f_arr, int n_objs) {
    // 计算总缓冲区大小
    size_t total_size = sizeof(int); // 开头存储数组元素个数
    for (int i = 0; i < n_objs; i++) {
        // 累加普通成员大小(排除ptr指针本身)
        total_size += sizeof(struct Foo) - sizeof(void*);
        // 累加ptr指向的实际数据大小
        total_size += f_arr[i].size_of_ptr;
    }

    char* buf = malloc(total_size);
    if (!buf) return NULL;

    // 写入元素个数
    *(int*)buf = n_objs;
    char* offset_ptr = buf + sizeof(int);

    for (int i = 0; i < n_objs; i++) {
        // 逐个写入普通成员,避免结构体对齐问题
        *(int*)offset_ptr = f_arr[i].a;
        offset_ptr += sizeof(int);
        *(int*)offset_ptr = f_arr[i].b;
        offset_ptr += sizeof(int);
        *(int*)offset_ptr = f_arr[i].size_of_ptr;
        offset_ptr += sizeof(int);
        *(double*)offset_ptr = f_arr[i].d;
        offset_ptr += sizeof(double);
        // 写入ptr指向的实际数据
        memcpy(offset_ptr, f_arr[i].ptr, f_arr[i].size_of_ptr);
        offset_ptr += f_arr[i].size_of_ptr;
    }

    return buf;
}

// 反序列化函数,返回堆分配的结构体数组,使用完需要先逐个释放ptr再释放数组本身
struct Foo* deserialize(void* buf) {
    char* offset_ptr = (char*)buf;
    // 读取数组元素个数
    int n_objs = *(int*)offset_ptr;
    offset_ptr += sizeof(int);

    struct Foo* new_arr = malloc(sizeof(struct Foo) * n_objs);
    if (!new_arr) return NULL;

    for (int i = 0; i < n_objs; i++) {
        // 读取普通成员
        new_arr[i].a = *(int*)offset_ptr;
        offset_ptr += sizeof(int);
        new_arr[i].b = *(int*)offset_ptr;
        offset_ptr += sizeof(int);
        new_arr[i].size_of_ptr = *(int*)offset_ptr;
        offset_ptr += sizeof(int);
        new_arr[i].d = *(double*)offset_ptr;
        offset_ptr += sizeof(double);
        // 为ptr分配堆内存
        new_arr[i].ptr = malloc(new_arr[i].size_of_ptr);
        if (!new_arr[i].ptr) {
            // 分配失败时释放已申请的内存,避免泄漏
            for (int j = 0; j < i; j++) {
                free(new_arr[j].ptr);
            }
            free(new_arr);
            return NULL;
        }
        // 拷贝实际数据到新分配的堆内存
        memcpy(new_arr[i].ptr, offset_ptr, new_arr[i].size_of_ptr);
        offset_ptr += new_arr[i].size_of_ptr;
    }

    return new_arr;
}

注意事项

  • 上述实现手动逐个拷贝成员,规避了结构体字节对齐带来的偏移问题,跨编译环境也可正常使用
  • 反序列化返回的结构体数组使用完后,需要先逐个释放每个元素的ptr指针,再释放结构体数组本身,避免内存泄漏
  • 原业务代码中Foo* f_arr = (Foo) malloc(sizeof Foo * n_objs_);存在类型转换错误,应改为Foo* f_arr = (Foo*) malloc(sizeof(struct Foo) * n_objs_);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.24 05:06:03