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

C语言:函数内为结构体数组多次调用malloc的内存分配问题

这是个非常典型的动态数组扩容问题,结合结构体内部的指针成员,你需要同时管理两层内存:结构体数组本身的存储空间,以及每个结构体中info指针指向的字符串内存。我来一步步给你拆解实现思路和可运行的代码:

核心思路

用realloc来动态调整结构体数组的大小,同时维护两个关键变量:

  • capacity:当前已分配的结构体总数量(数组的容量)
  • count:当前已经填充数据的结构体数量(已使用元素数)

当读取CSV的每一行时,先检查是否需要扩容(如果count >= capacity),扩容后再为新结构体的info分配内存,最后填充数据。这种方式可以高效处理未知行数的CSV,避免预先分配过大内存浪费空间。

分步实现细节

  1. 初始化变量:
    一开始可以把结构体数组指针设为NULL,capacity设为0,这样首次扩容时会自动分配初始容量(通常选4、8这类2的幂,后续每次翻倍,摊平扩容的时间成本)。

  2. 扩容逻辑:
    使用realloc时,绝对不要直接把返回值赋值给原指针——如果realloc失败会返回NULL,直接赋值会丢失原指针的地址,导致已分配的内存泄漏。正确做法是用临时指针接收结果,检查成功后再更新原指针和容量。

  3. 为info分配内存:
    每次新增结构体后,立刻为info分配内存。如果CSV中每个字段的长度不确定,用strdup会比固定分配50字节更灵活(strdup会自动根据字符串长度分配内存并复制内容);如果是固定长度,用malloc(50 * sizeof(char))即可,注意要手动添加字符串终止符。

  4. 内存清理:
    程序结束前,必须先释放每个结构体中info指向的内存,再释放结构体数组本身——顺序不能反,否则会导致野指针访问。

完整代码示例

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

typedef struct {
    int can_id;
    char* info;
} CAN_ID;

int main() {
    CAN_ID *can_id_list = NULL;
    size_t capacity = 0;  // 数组当前总容量
    size_t count = 0;     // 已填充的元素数量

    // 打开CSV文件(替换成你的文件路径)
    FILE *csv_file = fopen("can_data.csv", "r");
    if (!csv_file) {
        perror("Failed to open CSV file");
        return EXIT_FAILURE;
    }

    char line_buf[1024];
    // 逐行读取CSV
    while (fgets(line_buf, sizeof(line_buf), csv_file)) {
        // 去掉换行符
        line_buf[strcspn(line_buf, "\n")] = '\0';

        // 检查是否需要扩容
        if (count >= capacity) {
            // 首次扩容设为4,之后每次翻倍
            size_t new_cap = (capacity == 0) ? 4 : capacity * 2;
            CAN_ID *temp_ptr = realloc(can_id_list, new_cap * sizeof(CAN_ID));
            
            if (!temp_ptr) {
                perror("Failed to expand CAN_ID list");
                // 扩容失败,清理已分配的内存
                for (size_t i = 0; i < count; i++) {
                    free(can_id_list[i].info);
                }
                free(can_id_list);
                fclose(csv_file);
                return EXIT_FAILURE;
            }
            can_id_list = temp_ptr;
            capacity = new_cap;
        }

        // 为当前元素的info分配内存:这里用strdup更灵活,适配不同长度的字段
        char *csv_info = strtok(line_buf, ",");
        if (csv_info) {
            can_id_list[count].can_id = atoi(csv_info);
            csv_info = strtok(NULL, ",");
            
            if (csv_info) {
                can_id_list[count].info = strdup(csv_info);
            } else {
                can_id_list[count].info = strdup("No info provided");
            }
        } else {
            // 处理格式错误的行
            can_id_list[count].can_id = -1;
            can_id_list[count].info = strdup("Invalid line format");
        }

        // 检查info分配是否成功
        if (!can_id_list[count].info) {
            perror("Failed to allocate memory for info");
            // 清理已分配的内存
            for (size_t i = 0; i < count; i++) {
                free(can_id_list[i].info);
            }
            free(can_id_list);
            fclose(csv_file);
            return EXIT_FAILURE;
        }

        count++;
    }

    fclose(csv_file);

    // 打印测试结果
    printf("Loaded %zu CAN entries:\n", count);
    for (size_t i = 0; i < count; i++) {
        printf("Entry %zu: CAN ID = %d, Info = %s\n", i+1, can_id_list[i].can_id, can_id_list[i].info);
    }

    // 释放内存:先释放每个info,再释放数组
    for (size_t i = 0; i < count; i++) {
        free(can_id_list[i].info);
    }
    free(can_id_list);

    return EXIT_SUCCESS;
}

额外注意事项

  • 如果你的编译器不支持strdup(比如某些嵌入式环境),可以自己实现一个:
    char* my_strdup(const char* src) {
        size_t len = strlen(src) + 1;
        char* dest = malloc(len);
        if (dest) strcpy(dest, src);
        return dest;
    }
    
  • 尽量避免每次只扩容一个元素,频繁调用realloc会降低性能,按倍数扩容是行业通用的高效策略。
  • 所有内存分配操作后都要检查返回值,确保分配成功,否则会导致程序崩溃或内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.19 09:03:30