在 Ubuntu(或其他 Linux 系统)上,使用 opendir() / readdir() / closedir() 系列函数时,内存泄漏最常见的原因是“打开了目录却没有关闭”,或者误以为 readdir() 返回的结构体需要 free。
下面从原理、常见错误、正确写法、排查方法四个方面说明如何避免。
DIR *opendir(const char *name);
struct dirent *readdir(DIR *dirp);
int closedir(DIR *dirp);
内存规则:
opendir()DIR 结构(堆内存)closedir()readdir()struct dirent * 是内部缓冲区指针opendir 失败后仍 closedir(NULL)DIR *dir = opendir("/tmp");
if (!dir) return;
while (readdir(dir)) {
// 忘记 closedir
}
➡ 每次调用都会泄漏 DIR
DIR *dir = opendir("/not/exist");
closedir(dir); // ❌ NULL 指针
struct dirent *ent = readdir(dir);
free(ent); // ❌ 崩溃或内存破坏
#include <dirent.h>
#include <stdio.h>
void list_dir(const char *path) {
DIR *dir = opendir(path);
if (!dir) {
perror("opendir");
return;
}
struct dirent *ent;
while ((ent = readdir(dir)) != NULL) {
printf("%s\n", ent->d_name);
}
closedir(dir); // ✅ 必须
}
DIR *dir = opendir(path);
if (!dir) return;
// 多个 return 点
if (some_error) goto cleanup;
cleanup:
closedir(dir);
sudo apt install valgrind
valgrind --leak-check=full ./your_program
典型泄漏提示:
==12345== definitely lost: 4,096 bytes in 1 blocks
如果看到:
in opendir
说明你忘了 closedir
编译时:
gcc -fsanitize=address -g dir_test.c -o dir_test
运行:
./dir_test
grep -n "opendir" *.c
grep -n "closedir" *.c
数量必须一致(逻辑上)
if (ent->d_type == DT_DIR) {
char sub[PATH_MAX];
snprintf(sub, sizeof(sub), "%s/%s", path, ent->d_name);
list_dir(sub); // 每次都会 opendir
}
➡ 每一层都必须 closedir
opendir/readdir 不是线程安全readdir_r(旧)或加锁(推荐)避免 Ubuntu 下 opendir 内存泄漏的核心原则:
opendir 必须配对 closedir,readdir 的返回值不要 free。
如果你愿意,可以把你的 C 代码贴出来,我可以帮你精准指出哪一行在泄漏。