C语言:如何正确释放函数内分配字符串的指针数组?
堆内存损坏的根本原因
你执行循环free后触发malloc(): corrupted top size报错,核心原因是对malloc的内存分配逻辑理解错位:
- 你在
readlines里仅调用了1次p = malloc(sizeof(char) * MAXLENGTH),也就是只从堆上申请了1块总大小为1000字节的连续内存。 - 后续你通过
p += len+1做地址偏移,把这块内存的不同内部位置的地址,依次赋值给pointers[0]、pointers[1]…… 这些数组里的指针本质上都是指向同一块malloc内存的内部地址,根本不是独立malloc返回的指针。 - 你循环执行
free(pointers[i])时,第一次循环释放pointers[0](也就是malloc返回的原始首地址)是合法的,但从第二次循环开始,你释放的是同一块内存内部的偏移位置——而free只允许释放malloc/calloc/realloc返回的原始首地址,释放内部偏移地址会直接踩坏堆的元数据结构,触发程序崩溃。
哪些内存需要手动释放
- main函数里的
char *pointers[NUMOFLINES]是栈上分配的指针数组,它的内存会在main函数退出时由操作系统自动回收,完全不需要手动free。 - 你在readlines里通过malloc申请的那整块堆内存,是必须手动释放的——但只需要释放一次malloc返回的原始首地址即可,绝对不能循环释放数组里的每个元素。
代码里还存在一个潜在的越界风险:你申请的堆内存总大小只有1000字节,而单行的最大长度就可达1000字节,只要存入2行数据就会超出malloc的内存边界,踩坏堆结构,这也是触发报错的重要诱因。
正确的释放实现方案
方案1:保留“单块连续大内存存储所有行”的现有设计
你当前的设计本质是“一次性申请大内存,在内存块内部划分区域存每一行”,按这个设计修正的要点是:
- 在readlines中单独用一个变量保存malloc返回的原始首地址,避免因为p的地址偏移丢失原始地址,后续无法释放:
char *base_p; // 专门记录malloc返回的堆内存首地址 p = malloc(sizeof(char) * MAXLENGTH); base_p = p; // 固定存首地址,后续p的偏移操作不影响base_p - 把这个
base_p传回main函数,最后仅需执行一次free(base_p)就完成所有堆内存的释放,不需要循环遍历pointers数组做free操作。
方案2:调整为“每行单独申请独立内存块”的设计
如果你的本意是每一行内容都对应一块独立申请的堆内存,就需要把malloc的逻辑移到循环内部,为每一行单独申请刚好匹配长度的内存:
// readlines循环内的修正逻辑 for(iptr = 0 ; iptr < NUMOFLINES && (len = get_line(line)) > 0 ; iptr++) { // 单独为当前行申请内存:长度为len+1,预留字符串结束符的位置 char *line_mem = malloc(len + 1); strcpy(line_mem, line); pointers[iptr] = line_mem; // 数组内每个元素都是独立malloc返回的首地址 }
这种设计下,你最初写的循环free逻辑才是合法的:
// 独立内存块设计下的正确释放逻辑 for(int i = 0 ; i < nlines ; i++) free(pointers[i]);
内容的提问来源于stack exchange,提问作者spiazzy
相关产品推荐
相关产品推荐

