清理数组触发Double free错误,如何让MySettings的group和enable拥有独立地址?
问题解决:Double Free错误与结构体实例独立分配
问题根源分析
Double Free错误:
在read_db函数中,外层i循环每次仅创建一个MySettings实例st,随后内层g循环将同一个st指针重复赋值给Settings[0]到Settings[4]。当i递增时,后续创建的st会覆盖Settings数组中前5个位置的旧指针,导致旧实例内存泄漏;最终clear函数执行时,Settings[0]到Settings[4]指向的是同一个st实例(最后一次i循环创建的),多次调用free释放同一块内存,触发Double Free错误。实例地址重复问题:
所有group对应的Settings项都指向同一个st实例,自然地址相同。需要为每个group独立分配MySettings内存,并复制原实例的公共数据。
解决方案代码
#include <stdio.h> #include <stdlib.h> #include <string.h> struct Block { int id; }; struct MySettings { int conf_id; int group; int enable; int hide_name; int login_persistence; int remove_cache; struct Block *block; int block_count; // 新增字段记录block数组长度,方便复制 }; struct MySettings *Settings[50]; void clear(void) { puts("Clearing..."); for(int i = 0; i < 50; i++) { if(Settings[i]) { if(Settings[i]->block) free(Settings[i]->block); free(Settings[i]); Settings[i] = NULL; } } } int GetGroupID(int i) { return i; } // 辅助函数:复制MySettings实例(包含block数据) struct MySettings* copy_settings(const struct MySettings* src) { if (!src) return NULL; struct MySettings* dst = calloc(1, sizeof(*dst)); if (!dst) return NULL; // 复制基础字段 dst->conf_id = src->conf_id; dst->hide_name = src->hide_name; dst->login_persistence = src->login_persistence; dst->remove_cache = src->remove_cache; dst->block_count = src->block_count; // 复制block长度 // 复制block数组 if (src->block && src->block_count > 0) { dst->block = malloc(src->block_count * sizeof(*dst->block)); if (!dst->block) { free(dst); return NULL; } memcpy(dst->block, src->block, src->block_count * sizeof(*dst->block)); } return dst; } void read_db(void) { puts("Allocating..."); for(int i = 0; i < 10; i++) { struct MySettings *st = calloc(1, sizeof *st); if (!st) continue; st->conf_id = i; st->hide_name = 1; st->login_persistence = 1; st->remove_cache = 1; st->block_count = i + 1; // 记录当前block数组长度 // 分配并初始化block数组 st->block = malloc(st->block_count * sizeof *st->block); if (!st->block) { free(st); continue; } st->block[i].id = i+1; for(int g = 0; g < 5; g++) { // 为每个group创建独立的实例副本 struct MySettings* st_copy = copy_settings(st); if (!st_copy) continue; st_copy->group = GetGroupID(g); st_copy->enable = 1; printf("pointer %p group_id %d enable %s\n", st_copy, st_copy->group, st_copy->enable ? "true" : "false"); // 先释放旧指针(如果有的话),避免内存泄漏 if (Settings[st_copy->group]) { free(Settings[st_copy->group]->block); free(Settings[st_copy->group]); } Settings[st_copy->group] = st_copy; } // 释放原st,因为已经创建了副本 free(st->block); free(st); } clear(); } int main(void) { read_db(); puts("Done..."); return 0; }
关键修改点
- 新增
block_count字段:记录block数组的长度,方便复制时确定内存分配大小。 - 新增
copy_settings辅助函数:完整复制MySettings实例,包括动态分配的block数组,确保每个副本拥有独立的内存空间。 - 内层
g循环创建独立实例:不再复用同一个st,为每个group生成专属的实例副本,设置对应group和enable值。 - 及时释放原实例:外层
i循环创建的st在生成所有副本后立即释放,避免内存泄漏。 - 覆盖
Settings项前清理旧数据:防止未释放的旧实例内存泄漏。
内容的提问来源于stack exchange,提问作者Michael
相关产品推荐
相关产品推荐

