从文件读取结构体时出现Valgrind内存泄漏问题求助
内存泄漏问题分析与修复
Valgrind检测信息
==27091== HEAP SUMMARY: ==27091== in use at exit: 96 bytes in 4 blocks ==27091== total heap usage: 22 allocs, 18 frees, 11,347 bytes allocated ==27091== ==27091== 24 bytes in 1 blocks are definitely lost in loss record 1 of 2 ==27091== at 0x4847581: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==27091== by 0x1092C6: detailNew (in /home/students/s/smelov.vp/test5/lab5) ==27091== by 0x10944F: main (in /home/students/s/smelov.vp/test5/lab5) ==27091== ==27091== 72 bytes in 3 blocks are definitely lost in loss record 2 of 2 ==27091== at 0x4847581: calloc (in /usr/libexec/valgrind/vgpreload_memcheck-amd64-linux.so) ==27091== by 0x1092C6: detailNew (in /home/students/s/smelov.vp/test5/lab5) ==27091== by 0x109534: main (in /home/students/s/smelov.vp/test5/lab5) ==27091== ==27091== LEAK SUMMARY: ==27091== definitely lost: 96 bytes in 4 blocks ==27091== indirectly lost: 0 bytes in 0 blocks ==27091== possibly lost: 0 bytes in 0 blocks ==27091== still reachable: 0 bytes in 0 blocks ==27091== suppressed: 0 bytes in 0 blocks
问题根源分析
1. detailNew函数的内存泄漏
在detailNew中,你通过calloc动态分配了detail *item,但返回item[0](结构体拷贝)后,没有释放item指向的内存,导致每次调用detailNew都会泄漏一块sizeof(detail)大小的内存。输入文件有4行数据,对应4次调用,正好匹配Valgrind提示的4块96字节泄漏(64位系统下detail结构体大小为24字节:2个8字节指针+4字节int,对齐后为24)。
2. detailNew的野指针问题
你在给item->id和item->name赋值后,立即调用free(id)和free(name),导致返回的结构体中id和name成员指向已释放的内存(野指针),后续访问这些指针会触发未定义行为。
修复方案
重构detailNew函数
不需要动态分配detail对象,直接在栈上创建结构体,为id和name分配内存后赋值给结构体成员,返回结构体拷贝即可:
detail detailNew(char* idBuf, char* nameBuf, int pcs){ detail item; // 为id分配内存并拷贝内容 item.id = calloc(strlen(idBuf) + 1, sizeof(char)); strcpy(item.id, idBuf); // 为name分配内存并拷贝内容 item.name = calloc(strlen(nameBuf) + 1, sizeof(char)); strcpy(item.name, nameBuf); item.pcs = pcs; return item; }
释放结构体成员的动态内存
在main函数中,释放list数组前,需要先遍历每个元素,释放其id和name指向的动态内存,否则会产生新的内存泄漏:
// ... 原代码中其他逻辑 ... // 释放每个detail的id和name内存 for (size_t i = 0; i < amount; i++) { free(list[i].id); free(list[i].name); } free(list); return 0;
额外优化(非泄漏问题,但建议修复)
main中使用!feof(details)作为循环条件是错误的,应该以fscanf的返回值判断是否读取成功,否则会导致最后一行重复处理。修改后的循环逻辑如下:
size_t amount = 0; detail *list = NULL; int inputCheck; while ((inputCheck = fscanf(details, "%s %s %d", idBuf, nameBuf, &pcs)) == 3) { amount++; // 重新分配内存,用临时变量避免内存泄漏 detail *temp = realloc(list, amount * sizeof(detail)); if (temp == NULL) { // 内存分配失败时,先释放已分配的内存 for (size_t i = 0; i < amount-1; i++) { free(list[i].id); free(list[i].name); } free(list); fclose(details); return 1; } list = temp; list[amount-1] = detailNew(idBuf, nameBuf, pcs); }
内容的提问来源于stack exchange,提问作者Heat
相关产品推荐
相关产品推荐

