C语言:函数内为结构体数组多次调用malloc的内存分配问题
这是个非常典型的动态数组扩容问题,结合结构体内部的指针成员,你需要同时管理两层内存:结构体数组本身的存储空间,以及每个结构体中info指针指向的字符串内存。我来一步步给你拆解实现思路和可运行的代码:
核心思路
用realloc来动态调整结构体数组的大小,同时维护两个关键变量:
capacity:当前已分配的结构体总数量(数组的容量)count:当前已经填充数据的结构体数量(已使用元素数)
当读取CSV的每一行时,先检查是否需要扩容(如果count >= capacity),扩容后再为新结构体的info分配内存,最后填充数据。这种方式可以高效处理未知行数的CSV,避免预先分配过大内存浪费空间。
分步实现细节
初始化变量:
一开始可以把结构体数组指针设为NULL,capacity设为0,这样首次扩容时会自动分配初始容量(通常选4、8这类2的幂,后续每次翻倍,摊平扩容的时间成本)。扩容逻辑:
使用realloc时,绝对不要直接把返回值赋值给原指针——如果realloc失败会返回NULL,直接赋值会丢失原指针的地址,导致已分配的内存泄漏。正确做法是用临时指针接收结果,检查成功后再更新原指针和容量。为
info分配内存:
每次新增结构体后,立刻为info分配内存。如果CSV中每个字段的长度不确定,用strdup会比固定分配50字节更灵活(strdup会自动根据字符串长度分配内存并复制内容);如果是固定长度,用malloc(50 * sizeof(char))即可,注意要手动添加字符串终止符。内存清理:
程序结束前,必须先释放每个结构体中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
相关产品推荐
相关产品推荐

