C语言realloc调整字符串数组后如何正确释放动态内存
C语言动态路径列表的内存管理问题
问题描述
- 实现目标:编写C语言函数维护可动态更新的路径列表,初始化默认路径为
["/bin/"],通过change_paths函数可将路径列表替换为任意长度的新路径集合(包括空集合),例如将初始列表更新为["/temp/", "/auxillary/"] - 故障现象:程序末尾释放内存时,AddressSanitizer提示初始化阶段通过
paths[0] = malloc(6 * sizeof(char));分配的6字节内存未被正确释放 - 核心疑问:任意次数调用更新函数、传入任意数量新路径的场景下如何正确释放内存?当前使用的二级指针存储结构是否适配动态更新需求?
注:演示代码已做简化,例如
char** new_paths = {"/temp/", "/auxillary/"};严格来说存在语法问题,仅作场景演示,不纠结该写法的标准性。
原始问题代码
#include <stdio.h> #include <stdlib.h> #include <string.h> void change_paths(int *num_paths, char*** paths, int num_new_paths, char** new_paths){ *paths = realloc(*paths, num_new_paths * sizeof(char*)); for (int i = 0; i < num_new_paths; i++){ *paths[i] = malloc((strlen(new_paths[i]) + 1) * sizeof(char)); strcpy(*paths[i], new_paths[i]); } } int main(int argc, char *argv[]){ // 初始化默认路径 char** paths = malloc(1 * sizeof(char*)); paths[0] = malloc(6 * sizeof(char)); // <- realloc后这块内存如何释放? strcpy(paths[0], "/bin/"); int num_paths = 1; // 更新为新的路径集合 int num_new_paths = 2; char** new_paths = {"/temp/", "/auxillary/"}; change_paths(&num_paths, &paths, num_new_paths, new_paths); // 释放内存 for(int i = 0; i < num_new_paths; i++){ free(paths[i]); } free(paths); return 0; }
问题根因
当前用char**存储路径字符串指针数组、单独用整型变量记录路径数量的结构,完全适配动态更新需求,内存泄漏和报错来自三个代码逻辑错误:
- 旧内存未释放直接覆盖指针:调用
realloc调整指针数组大小前,没有释放数组中原有指针指向的路径字符串内存。初始状态下paths[0]指向存储"/bin/"的6字节堆内存,直接调整指针数组大小后,这块内存的引用丢失,成为无法回收的泄漏内存,也就是Sanitizer报的问题。 - 运算符优先级错误:
*paths[i]的写法等价于*(paths[i]),但传入的paths是三级指针char***,正确的访问方式是先解引用拿到二级指针,再访问下标,即(*paths)[i],原写法属于非法内存访问,触发未定义行为。 - 状态不同步:更新路径后没有修改
*num_paths的计数值,同时没有处理num_new_paths = 0的边界场景,后续释放时循环边界错误,要么漏释放要么越界访问。
修正实现
修正逻辑遵循“覆盖指针前先释放旧内存”的原则:
- 更新路径前,先遍历释放当前列表中所有路径字符串的堆内存
- 单独处理新路径数量为0的场景,避免realloc分配0长度内存的实现兼容问题
- 修正指针访问的优先级错误,为新路径分配内存后拷贝内容
- 每次更新完成后同步修改路径计数,保证后续释放逻辑的循环边界正确
修正后的完整代码:
#include <stdio.h> #include <stdlib.h> #include <string.h> void change_paths(int *num_paths, char*** paths, int num_new_paths, char** new_paths){ // 第一步:释放所有旧的路径字符串内存 for (int i = 0; i < *num_paths; i++) { free((*paths)[i]); } // 处理新路径数为0的边界场景 if (num_new_paths == 0) { free(*paths); *paths = NULL; *num_paths = 0; return; } // 调整指针数组大小,用临时变量接收返回值避免realloc失败丢失原指针 char** temp = realloc(*paths, num_new_paths * sizeof(char*)); if (temp == NULL) { perror("realloc failed"); exit(1); } *paths = temp; // 分配新路径的内存并拷贝内容 for (int i = 0; i < num_new_paths; i++){ (*paths)[i] = malloc((strlen(new_paths[i]) + 1) * sizeof(char)); strcpy((*paths)[i], new_paths[i]); } // 更新路径计数 *num_paths = num_new_paths; } // 通用释放逻辑,任意状态下调用都不会泄漏 void free_paths(char** paths, int num_paths) { for (int i = 0; i < num_paths; i++) { free(paths[i]); } free(paths); } int main(int argc, char *argv[]){ // 初始化默认路径 char** paths = malloc(1 * sizeof(char*)); paths[0] = malloc(6 * sizeof(char)); strcpy(paths[0], "/bin/"); int num_paths = 1; // 测试1:更新为2个路径 char* new_paths1[] = {"/temp/", "/auxillary/"}; change_paths(&num_paths, &paths, 2, new_paths1); // 测试2:更新为0个路径 change_paths(&num_paths, &paths, 0, NULL); // 测试3:更新为3个路径 char* new_paths2[] = {"/usr/bin/", "/home/user/", "/opt/"}; change_paths(&num_paths, &paths, 3, new_paths2); // 统一释放 free_paths(paths, num_paths); return 0; }
内容的提问来源于stack exchange,提问作者kpz
相关产品推荐
相关产品推荐

