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

C语言链表valgrind提示list_add中malloc内存泄漏排查

Valgrind 报告链表内存泄漏问题排查

问题现象

Valgrind 检测到list_add函数中通过malloc分配的16字节内存发生明确泄漏,泄漏调用栈指向list_add中为新节点分配内存的代码行,但表面看节点已经正确挂入链表,且实现了递归释放函数free_list,逻辑上似乎不存在泄漏条件。
链表设计规则:头节点(newlist返回的初始节点)的object始终为NULL,实际存储业务数据的节点从头节点的后继节点开始存放。

相关代码

链表结构体定义(list.h)

typedef struct _linekd_list{
    void* object;
    struct _linked_list* next;
}linked_list;

注意:此处结构体标签存在拼写错误,_linekd_list应为_linked_list,否则会触发编译类型不匹配问题

链表初始化函数(list.c)

linked_list* newlist(){
    linked_list * list = malloc(sizeof(linked_list));
    list->next = NULL;  // 作为链表结束标记
    list->object = NULL;
    return list;
}

链表添加元素函数(list.c)

int list_add(void* new_object, linked_list* list){
    while(list->next != NULL) {  // 遍历到队列尾部
        list = list->next;
    }
    list->next = malloc(sizeof(linked_list)); // Valgrind 提示此处分配的内存泄漏
    list->next->next = NULL; // 新节点设置结束标记
    list->next->object = new_object; // 存储实际对象指针
    if(list->next->object == new_object) {
        return 0;
    } else {
        return 1;
    }
    return 0;
}

Valgrind 泄漏报告

==33369== 16 bytes in 1 blocks are definitely lost in loss record 1 of 3
==33369==    at 0x483B7F3: malloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==33369==    by 0x402219: list_add (list.c:11)
==33369==    by 0x4012D0: main (test_list.c:38)

链表释放函数(list.c)

void free_list(linked_list* list){
    if(list->next != NULL) {
        free_list(list->next);
        free(list);
    }
}

泄漏根因

泄漏和list_add的内存分配逻辑无关,完全是free_list函数的逻辑错误导致:

  • 现有释放逻辑仅当节点的next指针不为空时,才会递归释放后继节点、再释放当前节点。
  • 这意味着所有next为NULL的尾节点(包括仅存头节点的场景下的头节点本身)永远不会被释放:
    • 当链表只有newlist创建的头节点时,头节点next为NULL,调用free_list直接跳过所有逻辑,头节点直接泄漏。
    • 当链表添加过元素后,遍历到最后一个实际存储数据的节点时,该节点next为NULL,递归到这一层直接返回,不会执行free操作,最后一个节点永久泄漏,刚好对应Valgrind报告的16字节(单节点大小)泄漏。
  • Valgrind把泄漏点记在list_add的malloc行,是因为这块内存是在这里分配的,但本质是释放逻辑没有覆盖到这块内存。

修复方案

  • 修正free_list的释放逻辑,确保所有节点都能被释放,参考实现:
void free_list(linked_list* list){
    if (list == NULL) {
        return;
    }
    // 先递归释放后继节点
    free_list(list->next);
    // 无论next是否为空,当前节点都需要释放
    free(list);
}
  • 修复结构体定义的拼写错误,避免类型不匹配问题:
typedef struct _linked_list{
    void* object;
    struct _linked_list* next;
}linked_list;
  • 可选优化:补充list_add的入参校验和malloc异常判断,避免空指针访问,同时删除函数末尾永远无法执行的冗余return 0:
int list_add(void* new_object, linked_list* list){
    if (list == NULL) {
        return 1;
    }
    while(list->next != NULL) {
        list = list->next;
    }
    list->next = malloc(sizeof(linked_list));
    if (list->next == NULL) {
        return 1; // 内存分配失败
    }
    list->next->next = NULL;
    list->next->object = new_object;
    return 0;
}

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.30 04:33:26