C程序中明显存在的内存泄漏问题

6

请问有人能解释一下为什么Valgrind将这个程序分类为“Definitely Lost: 2 bytes in 1 block”内存泄漏吗?我知道注释掉的那行代码可以解决问题,但是我不理解这种分类。根据Valgrind文档,这个内存泄漏应该被分类为“间接可达”。我也很好奇为什么这被认为是内存泄漏,并且希望得到解释。尽管程序在main函数结束时终止,手动释放所有内容是否是一个好习惯呢?

#include <stdlib.h>

struct wrapper {
  char *data;
};

char *strdup(const char *);

struct wrapper *walloc(struct wrapper *root)
{
  if (root == NULL){
    root = (struct wrapper *) malloc(sizeof(struct wrapper));
    root->data = strdup("H");
  }

  return root;
}

int main(){
  struct wrapper *root;

  root = NULL;
  root = walloc(root);

  //free(root->data);

  return 0;
}

以下是Valgrind的输出:

$ valgrind --leak-check=full ./leak
==26489== Memcheck, a memory error detector
==26489== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==26489== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==26489== Command: ./leak
==26489==
==26489==
==26489== HEAP SUMMARY:
==26489==     in use at exit: 2 bytes in 1 blocks
==26489==   total heap usage: 2 allocs, 1 frees, 1,790 bytes allocated
==26489==
==26489== 2 bytes in 1 blocks are definitely lost in loss record 1 of 1
==26489==    at 0x4C29F90: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==26489==    by 0x4EB79C9: strdup (in /usr/lib/libc-2.20.so)
==26489==    by 0x400542: walloc (leak.c:13)
==26489==    by 0x400542: main (leak.c:23)
==26489==
==26489== LEAK SUMMARY:
==26489==    definitely lost: 2 bytes in 1 blocks
==26489==    indirectly lost: 0 bytes in 0 blocks
==26489==      possibly lost: 0 bytes in 0 blocks
==26489==    still reachable: 0 bytes in 0 blocks
==26489==         suppressed: 0 bytes in 0 blocks
==26489==
==26489== For counts of detected and suppressed errors, rerun with: -v
==26489== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

2
“即使程序在主函数结束时终止,手动释放所有内容是否是一个好的实践?” 是的,总是这样做,即使操作系统释放了内存。 free() 可以暴露程序中难以找到的错误。始终在分配代码完成后直接实现释放是一个好习惯。 - Lundin
2
我没有看到这个。我看到了8个字节的明确丢失(root)和2个字节的间接丢失(root->data)。你能发布来自valgrind的完整输出吗? - Bill Lynch
你应该#include <string.h>,因为编译器可以对strdup进行一些“神奇”的操作(有时候GCC会这样做)。 - Basile Starynkevitch
@sharth 我已经包含了Valgrind的输出。 - Pan Thomakos
@PanThomakos:你是用 -O3 编译的吗? - Bill Lynch
是的,CFLAGS = -Wall -O3 -pg。如果我不使用它进行编译,我会得到不同的输出——直接8字节和间接2字节。我现在知道问题出在哪里了。 - Pan Thomakos
2个回答

3

strdup在堆上分配内存(使用malloc),因此您需要像对malloc的任何直接调用一样,在不再使用时释放这些内存。

即使程序完成,您也必须这样做,因为这是检测内存泄漏的唯一方法。当一个程序结束时,它所分配的所有内存通常会自动被操作系统释放,因此检查任何内存泄漏的概念可能有些过度,但请不要忘记,您的小程序在这里是个例外。通常,大多数程序在运行时将占用大量内存,如果其中存在多个内存泄漏,它们可能会耗尽内存或运行得慢得多。

即使是小型程序也应该编写得很好;否则,您将无法编写任何大型程序,因为您的坏习惯将转化为大量编码错误。


感谢您的回复。为什么Valgrind将这个字符串嵌套在结构体中报告为直接泄漏?此外,为什么Valgrind不会抱怨释放为root分配的内存? - Pan Thomakos
抱歉,我不使用Valgrind,因此对它使用的各种定义一无所知。也许为root分配的内存是它所称的“丢失记录”,但您应该查阅文档或在专门针对Valgrind的论坛中提问,或者在这里撰写另一个关于Valgrind的问题。 - SylvainL

2
感谢@sharth指引我正确的方向。Valgrind实际上已经正确检测到了直接损失,但由于编译参数-O3root完全移除,导致输出结果令人困惑。如果不使用-O3进行编译,则会显示8字节的正确直接损失和2字节的间接损失。
同时,感谢@SylvainL和@Lundin提供的最佳实践建议。
FYI:更正后的Valgrind输出如下:
==30492== Memcheck, a memory error detector
==30492== Copyright (C) 2002-2013, and GNU GPL'd, by Julian Seward et al.
==30492== Using Valgrind-3.10.0 and LibVEX; rerun with -h for copyright info
==30492== Command: ./leak
==30492==
==30492==
==30492== HEAP SUMMARY:
==30492==     in use at exit: 10 bytes in 2 blocks
==30492==   total heap usage: 3 allocs, 1 frees, 1,830 bytes allocated
==30492==
==30492== 10 (8 direct, 2 indirect) bytes in 1 blocks are definitely lost in loss record 2 of 2
==30492==    at 0x4C29F90: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==30492==    by 0x400687: walloc (leak.c:12)
==30492==    by 0x4006C6: main (leak.c:23)
==30492==
==30492== LEAK SUMMARY:
==30492==    definitely lost: 8 bytes in 1 blocks
==30492==    indirectly lost: 2 bytes in 1 blocks
==30492==      possibly lost: 0 bytes in 0 blocks
==30492==    still reachable: 0 bytes in 0 blocks
==30492==         suppressed: 0 bytes in 0 blocks
==30492==
==30492== For counts of detected and suppressed errors, rerun with: -v
==30492== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接