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

含指针的嵌套结构Memcpy引发double free的无泄漏修复方案

解决C代码中的Double Free崩溃与内存泄漏问题

问题描述

这段C代码运行时触发double free崩溃,需求是把栈上数组tmp的内容复制到list结构体中。若删除get_copy函数里的free(tmp[i].p1)可避免崩溃,但会造成内存泄漏。已知代码存在三次内存分配(忽略循环)导致重复释放,也清楚memcpy仅复制内存值而非深层内容,但不知道如何正确释放先前分配的内存。

问题根源

  1. 指针覆盖引发双重释放:
    你先通过calloc给list->v1[i].p1分配了内存,随后执行memcpy(list->v1, tmp, sizeof(list->v1))——这里sizeof(list->v1)是指针的大小(64位系统下为8字节),无法完成5个tmp_data的完整复制。更关键的是,即便长度正确,该操作会直接把tmp[i].p1的指针值复制到list->v1[i].p1中,覆盖掉之前calloc生成的指针,造成内存泄漏。之后你free(tmp[i].p1),相当于提前释放了list->v1[i].p1指向的内存,到main函数中再次free(l1.v1[i].p1)时,就触发了double free。
  2. memcpy长度错误:sizeof(list->v1)获取的是指针大小,而非5个tmp_data的总字节数,正确长度应为sizeof(tmp_data) * list->num。

两种解决方案

方案1:转移内存所有权(避免重复分配)

既然tmp是栈上数组,前5个元素的p1指向堆内存,我们可以直接将这些指针的所有权转移给list->v1,无需为list->v1[i].p1重新分配内存,既不会泄漏也不会重复释放:

void get_copy(tmp_list *list) {
    tmp_data tmp[10];
    int i;
    for(i=0;i<10;i++){
        tmp[i].p1= calloc(1,sizeof(tmp_type));
        tmp[i].p1->x = tmp[i].p1->y = tmp[i].p1->z = 6;
        tmp[i].a = 'a'+i;
    }

    list->num = 5;
    // 仅分配存放5个tmp_data的空间,无需单独给p1分配内存
    list->v1 = calloc(list->num, sizeof(tmp_data));
    
    // 复制前5个tmp_data的完整内容(包括a字段和p1指针)
    memcpy(list->v1, tmp, sizeof(tmp_data) * list->num);
    
    // 仅释放tmp中未被转移的p1(索引5到9)
    for(i=5;i<10;i++) {
        free(tmp[i].p1);
    }
}

此时list->v1[i].p1直接接管tmp[i].p1的内存,main函数中释放这些指针是安全的,无double free或内存泄漏问题。

方案2:深层复制(保留list的独立内存)

若希望list拥有完全独立的内存空间,不依赖tmp的指针,需逐个复制结构体字段,避免覆盖list->v1[i].p1的指针:

void get_copy(tmp_list *list) {
    tmp_data tmp[10];
    int i;
    for(i=0;i<10;i++){
        tmp[i].p1= calloc(1,sizeof(tmp_type));
        tmp[i].p1->x = tmp[i].p1->y = tmp[i].p1->z = 6;
        tmp[i].a = 'a'+i;
    }

    list->num = 5;
    list->v1 = calloc(list->num, sizeof(tmp_data));
    
    // 逐个复制字段,避免覆盖p1指针
    for(i=0;i<list->num;i++) {
        // 为list的p1分配独立内存
        list->v1[i].p1 = calloc(1, sizeof(tmp_type));
        // 复制tmp_type的内容
        memcpy(list->v1[i].p1, tmp[i].p1, sizeof(tmp_type));
        // 复制char类型的a字段
        list->v1[i].a = tmp[i].a;
        // 释放tmp的p1,内容已完成复制
        free(tmp[i].p1);
    }
    
    // 释放tmp中剩余未使用的p1
    for(i=5;i<10;i++) {
        free(tmp[i].p1);
    }
}

这里去掉了错误的批量memcpy操作,改为逐个复制结构体的每个字段,确保list->v1[i].p1指向自身分配的内存。复制完成后释放tmp的p1,main函数中释放list的p1不会引发问题,也无内存泄漏。

内容的提问来源于stack exchange,提问作者LostProgrammer

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 04:06:02