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

清理数组触发Double free错误,如何让MySettings的group和enable拥有独立地址?

问题解决:Double Free错误与结构体实例独立分配

问题根源分析

  1. 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错误。

  2. 实例地址重复问题:
    所有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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 16:27:49