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

如何在C语言中正确存储含指针成员的结构体到文件?

解决C语言中含指针的结构体序列化/反序列化问题

你说得对,直接把结构体写入文件只会保存指针的内存地址——这些地址在程序下次运行时完全无效,自然会导致段错误。要解决这个问题,我们需要手动序列化指针指向的实际数据,同时还要记录数据的长度信息(因为C无法自动追踪动态分配内存的大小)。下面是具体的实现步骤和代码示例:

前提:明确数据的长度信息

首先,你需要知道每个指针指向的数据的维度/长度:

  • 对于char **one(二维字符串数组):需要知道总行数,以及每行字符串的长度(或者直接用字符串的\0作为结束标记,但提前存长度更可靠)
  • 对于mytype **two(二维自定义类型数组):需要知道总行数和每行的元素个数
  • 对于mytype2 *three(一维自定义类型数组):需要知道元素总个数

假设我们在获取结构体时,同时记录这些长度信息(你可以修改get_struct函数返回这些信息,或者用全局变量/额外的结构体存储,这里我们用额外的变量来传递)。


步骤1:实现结构体的保存(序列化)

我们需要按顺序写入:元数据(长度信息)→ 每个指针指向的实际数据。

示例代码:

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

// 先定义你的自定义类型(假设mytype和mytype2是这样的)
typedef int mytype;
typedef float mytype2;

typedef struct myStruct {
    char **one;
    mytype **two;
    mytype2 *three;
} MyStruct;

// 假设get_struct会返回结构体,同时我们需要获取这些长度信息:
void get_struct(MyStruct *s, int *rows_one, int *rows_two, int *cols_two, int *count_three) {
    // 这里模拟动态分配数据
    *rows_one = 2;
    s->one = malloc(*rows_one * sizeof(char*));
    s->one[0] = strdup("Hello");
    s->one[1] = strdup("World");

    *rows_two = 2;
    *cols_two = 3;
    s->two = malloc(*rows_two * sizeof(mytype*));
    for (int i = 0; i < *rows_two; i++) {
        s->two[i] = malloc(*cols_two * sizeof(mytype));
        for (int j = 0; j < *cols_two; j++) {
            s->two[i][j] = i * 10 + j;
        }
    }

    *count_three = 4;
    s->three = malloc(*count_three * sizeof(mytype2));
    for (int i = 0; i < *count_three; i++) {
        s->three[i] = i * 0.5f;
    }
}

// 保存结构体到文件的函数
int save_struct(MyStruct *s, const char *filename, int rows_one, int rows_two, int cols_two, int count_three) {
    FILE *fp = fopen(filename, "wb"); // 用二进制模式打开!
    if (!fp) {
        perror("Failed to open file for writing");
        return -1;
    }

    // 第一步:写入元数据(所有长度信息)
    fwrite(&rows_one, sizeof(int), 1, fp);
    fwrite(&rows_two, sizeof(int), 1, fp);
    fwrite(&cols_two, sizeof(int), 1, fp);
    fwrite(&count_three, sizeof(int), 1, fp);

    // 第二步:写入char **one的数据
    for (int i = 0; i < rows_one; i++) {
        // 先写入字符串长度(包括'\0'),避免读取时依赖'\0'
        size_t str_len = strlen(s->one[i]) + 1;
        fwrite(&str_len, sizeof(size_t), 1, fp);
        fwrite(s->one[i], sizeof(char), str_len, fp);
    }

    // 第三步:写入mytype **two的数据
    for (int i = 0; i < rows_two; i++) {
        fwrite(s->two[i], sizeof(mytype), cols_two, fp);
    }

    // 第四步:写入mytype2 *three的数据
    fwrite(s->three, sizeof(mytype2), count_three, fp);

    fclose(fp);
    return 0;
}

步骤2:实现结构体的读取(反序列化)

读取时需要按保存的顺序:先读元数据→分配对应内存→读取实际数据到分配的内存中。

示例代码:

// 读取结构体从文件的函数
int load_struct(MyStruct *s, const char *filename, int *rows_one, int *rows_two, int *cols_two, int *count_three) {
    FILE *fp = fopen(filename, "rb"); // 二进制模式读取!
    if (!fp) {
        perror("Failed to open file for reading");
        return -1;
    }

    // 第一步:读取元数据
    fread(rows_one, sizeof(int), 1, fp);
    fread(rows_two, sizeof(int), 1, fp);
    fread(cols_two, sizeof(int), 1, fp);
    fread(count_three, sizeof(int), 1, fp);

    // 第二步:读取char **one
    s->one = malloc(*rows_one * sizeof(char*));
    for (int i = 0; i < *rows_one; i++) {
        size_t str_len;
        fread(&str_len, sizeof(size_t), 1, fp);
        s->one[i] = malloc(str_len * sizeof(char));
        fread(s->one[i], sizeof(char), str_len, fp);
    }

    // 第三步:读取mytype **two
    s->two = malloc(*rows_two * sizeof(mytype*));
    for (int i = 0; i < *rows_two; i++) {
        s->two[i] = malloc(*cols_two * sizeof(mytype));
        fread(s->two[i], sizeof(mytype), *cols_two, fp);
    }

    // 第四步:读取mytype2 *three
    s->three = malloc(*count_three * sizeof(mytype2));
    fread(s->three, sizeof(mytype2), *count_three, fp);

    fclose(fp);
    return 0;
}

步骤3:测试使用,以及内存释放

别忘了使用完结构体后要释放所有动态分配的内存,避免泄漏:

// 释放结构体内存的函数
void free_struct(MyStruct *s, int rows_one, int rows_two, int count_three) {
    // 释放one
    for (int i = 0; i < rows_one; i++) {
        free(s->one[i]);
    }
    free(s->one);

    // 释放two
    for (int i = 0; i < rows_two; i++) {
        free(s->two[i]);
    }
    free(s->two);

    // 释放three
    free(s->three);
}

// 主函数测试
int main() {
    MyStruct s, t;
    int rows_one, rows_two, cols_two, count_three;

    // 获取结构体
    get_struct(&s, &rows_one, &rows_two, &cols_two, &count_three);

    // 保存到文件
    if (save_struct(&s, "file.dat", rows_one, rows_two, cols_two, count_three) != 0) {
        exit(EXIT_FAILURE);
    }

    // 释放原结构体内存
    free_struct(&s, rows_one, rows_two, count_three);

    // 从文件读取结构体
    if (load_struct(&t, "file.dat", &rows_one, &rows_two, &cols_two, &count_three) != 0) {
        exit(EXIT_FAILURE);
    }

    // 调用你的work_with_struct函数
    work_with_struct(t);

    // 读取后释放内存
    free_struct(&t, rows_one, rows_two, count_three);

    return 0;
}

// 你的work_with_struct函数
void work_with_struct(MyStruct s){
    char ** xone = s.one;
    mytype **xtwo = s.two;
    mytype2 *xthree = s.three;

    // 这里可以测试输出数据是否正确
    printf("one[0]: %s\n", xone[0]);
    printf("one[1]: %s\n", xone[1]);
    for (int i = 0; i < 2; i++) {
        for (int j = 0; j < 3; j++) {
            printf("two[%d][%d]: %d ", i, j, xtwo[i][j]);
        }
        printf("\n");
    }
    for (int i = 0; i < 4; i++) {
        printf("three[%d]: %.1f\n", i, xthree[i]);
    }
}

关键注意事项

  • 必须用二进制模式打开文件("wb"和"rb"),避免文本模式下的换行符转换等问题,导致数据错位。
  • 元数据的顺序必须和保存时一致,读取时如果顺序错了,所有数据都会乱掉。
  • 内存管理要仔细:分配的每一块内存都要对应释放,尤其是二维数组的行指针和每行的内存。
  • 如果你的mytype或mytype2是复杂结构体(而非基本类型),那你需要对它们也做序列化/反序列化(如果它们内部也有指针的话)——如果只是基本类型(int、float等),直接fwrite/fread就行。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.08 17:32:32