C中递归函数中的内存泄漏
Memory leak in recursive function in C
我的小 C 程序正在遍历工作目录的内容,这没有问题,但我用 Valgrind 检查了它,发现有一些神秘的漏洞,我好几天都无法消除。
代码如下:
struct record {
char* fullPath;
int wdlen; // working directory length
int relplen; // relative path length
int namlen; // name length
bool folder; // is folder
long modified;
};
void populateDB() {
struct record** records = NULL;
int entryCount = 0;
bool error = false;
listDirRecursive(workingDirectory, &entryCount, &records, &error);
if (error) {
goto cleanup;
}
for (int i = 0; i < entryCount; i++) {
printfColor(C_MAGENTA, "%d. wdlen: %d,\t\trelPathLen: %d,\t\tnamelen: %d,\t\tfolder: %s,\t\tmod: %ld,\t\tpath: %s\n",
i, records[i]->wdlen, records[i]->relplen, records[i]->namlen, records[i]->folder ? "true" : "false", records[i]->modified, records[i]->fullPath);
}
// initDatabase(&entryCount, &records);
cleanup:
for (int i = 0; i < entryCount; i++) {
free(records[i]->fullPath);
free(records[i]);
}
free(records);
}
void listDirRecursive(const char* path, int* count, struct record*** records, bool* error) {
if (*error) return;
struct dirent** fileList = NULL;
int fileCount = scandir(path, &fileList, NULL, alphasort);
if (fileCount < 0) {
printfColor(C_RED, "Couldn't open: %s\n", path);
*error = true;
return;
}
struct record** tmpRecords = malloc(fileCount * sizeof *tmpRecords);
if (!tmpRecords) {
printfColor(C_RED, "Couldn't allocate memory for records\n");
*error = true;
return;
}
struct record* act = NULL;
struct stat filestat;
int processedFiles = 0;
for (int i = 0, wdlen = strlen(workingDirectory), plen = strlen(path); i < fileCount; i++) {
if (strcmp(fileList[i]->d_name, ".") == 0 ||
strcmp(fileList[i]->d_name, "..") == 0) continue;
act = malloc(sizeof *act);
if (!act) {
printfColor(C_RED, "Couldn't allocate memory for actual record\n");
*error = true;
goto cleanup;
}
act->fullPath = malloc(plen + fileList[i]->d_namlen + 2);
if (!act->fullPath) {
printfColor(C_RED, "Couldn't allocate memory for actual record's full path\n");
*error = true;
goto cleanup;
}
strcat(strcat(strcpy(act->fullPath, path), "/"), fileList[i]->d_name);
act->wdlen = wdlen + 1;
act->relplen = strlen(act->fullPath) - ((wdlen + 1) + fileList[i]->d_namlen);
act->namlen = fileList[i]->d_namlen;
act->folder = fileList[i]->d_type == DT_DIR;
stat(act->fullPath, &filestat);
act->modified = filestat.st_mtimespec.tv_nsec;
tmpRecords[processedFiles++] = act;
if (fileList[i]->d_type == DT_DIR) {
char* p = malloc(plen + fileList[i]->d_namlen + 2);
strcat(strcat(strcpy(p, path), "/"), fileList[i]->d_name);
listDirRecursive(p, count, records, error);
free(p);
}
}
if (processedFiles == 0) {
goto cleanup;
}
struct record** shrinkedTmpRecords = realloc(tmpRecords, processedFiles * sizeof *shrinkedTmpRecords);
if (!shrinkedTmpRecords) {
if (verbose) printf("Shrinking tmpRecords failed. Some memory leak will happen\n");
}
else {
tmpRecords = shrinkedTmpRecords;
}
int newCount = *count + processedFiles;
if (*count == 0) {
*records = tmpRecords;
}
else {
struct record** newRecords = realloc(*records, newCount * sizeof *newRecords);
if (!newRecords) {
printfColor(C_RED, "Couldn't allocate memory for further records\n");
*error = true;
goto cleanup;
}
*records = newRecords;
for (int i = 0, j = *count; i < processedFiles; i++, j++) {
(*records)[j] = tmpRecords[i];
}
}
*count = newCount;
cleanup:
for (int i = 0; i < fileCount; i++) {
free(fileList[i]);
}
free(fileList);
}
在运行之后,Valgrind的输出是这样的:
==40861== HEAP SUMMARY:
==40861== in use at exit: 18,084 bytes in 170 blocks
==40861== total heap usage: 423 allocs, 253 frees, 180,357 bytes allocated
==40861==
==40861== 8 bytes in 1 blocks are definitely lost in loss record 1 of 43
==40861== at 0x100111FD6: realloc (in /usr/local/Cellar/valgrind/HEAD-e0af3eb/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==40861== by 0x10000162B: listDirRecursive (filesystem.c:137)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x100001092: populateDB (filesystem.c:44)
==40861== by 0x100000E15: init (mtl.c:111)
==40861== by 0x100000CD2: main (mtl.c:61)
==40861==
==40861== 16 bytes in 2 blocks are definitely lost in loss record 5 of 43
==40861== at 0x100111FD6: realloc (in /usr/local/Cellar/valgrind/HEAD-e0af3eb/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==40861== by 0x10000162B: listDirRecursive (filesystem.c:137)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x100001092: populateDB (filesystem.c:44)
==40861== by 0x100000E15: init (mtl.c:111)
==40861== by 0x100000CD2: main (mtl.c:61)
==40861==
==40861== 72 bytes in 5 blocks are definitely lost in loss record 22 of 43
==40861== at 0x100111FD6: realloc (in /usr/local/Cellar/valgrind/HEAD-e0af3eb/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==40861== by 0x10000162B: listDirRecursive (filesystem.c:137)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x100001092: populateDB (filesystem.c:44)
==40861== by 0x100000E15: init (mtl.c:111)
==40861== by 0x100000CD2: main (mtl.c:61)
==40861==
==40861== 80 bytes in 1 blocks are definitely lost in loss record 23 of 43
==40861== at 0x100111FD6: realloc (in /usr/local/Cellar/valgrind/HEAD-e0af3eb/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==40861== by 0x10000162B: listDirRecursive (filesystem.c:137)
==40861== by 0x100001092: populateDB (filesystem.c:44)
==40861== by 0x100000E15: init (mtl.c:111)
==40861== by 0x100000CD2: main (mtl.c:61)
==40861==
==40861== 112 bytes in 4 blocks are definitely lost in loss record 26 of 43
==40861== at 0x100111FD6: realloc (in /usr/local/Cellar/valgrind/HEAD-e0af3eb/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==40861== by 0x10000162B: listDirRecursive (filesystem.c:137)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x100001092: populateDB (filesystem.c:44)
==40861== by 0x100000E15: init (mtl.c:111)
==40861== by 0x100000CD2: main (mtl.c:61)
==40861==
==40861== LEAK SUMMARY:
==40861== definitely lost: 288 bytes in 13 blocks
==40861== indirectly lost: 0 bytes in 0 blocks
==40861== possibly lost: 0 bytes in 0 blocks
==40861== still reachable: 0 bytes in 0 blocks
==40861== suppressed: 17,796 bytes in 157 blocks
288字节除以8(指针的大小)为36,即末尾的总记录(文件和文件夹)。 13 个块数等于文件夹数。
我尝试了我想到的所有方法,但没有任何效果,我正在学习 C,所以我可能不知道一些重要的东西(我在主题中阅读了很多并试图避免大否-编号).
如果有人不明白那是什么 printfColor
,这里是:
// header file:
enum Color {
C_RED,
C_BOLD_RED,
C_GREEN,
C_BOLD_GREEN,
C_YELLOW,
C_BOLD_YELLOW,
C_BLUE,
C_BOLD_BLUE,
C_MAGENTA,
C_BOLD_MAGENTA,
C_CYAN,
C_BOLD_CYAN,
C_RESET
};
static const char* colors[] = {
"3[0;31m", // RED
"3[1;31m", // BOLD RED
"3[0;32m", // GREEN
"3[1;32m", // BOLD GREEN
"3[0;33m", // YELLOW
"3[01;33m", // BOLD YELLOW
"3[0;34m", // BLUE
"3[1;34m", // BOLD BLUE
"3[0;35m", // MAGENTA
"3[1;35m", // BOLD MAGENTA
"3[0;36m", // CYAN
"3[1;36m", // BOLD CYAN
"3[0m" // RESET
};
// c file:
void printfColor(enum Color color, const char* format, ...) {
va_list args;
printf("%s", colors[color]);
va_start(args, format);
vprintf(format, args);
va_end(args);
printf("%s", colors[C_RESET]);
}
干杯
Shawn 的评论是正确的。 tmpRecords 被分配并用作一组指针:
struct record** shrinkedTmpRecords = realloc(tmpRecords, processedFiles * sizeof *shrinkedTmpRecords);
if (!shrinkedTmpRecords) {
if (verbose) printf("Shrinking tmpRecords failed. Some memory leak will happen\n");
}
else {
tmpRecords = shrinkedTmpRecords;
}
然后,如果计数为 0,您只是将 records
分配给 tmpRecords
:
if (*count == 0) {
*records = tmpRecords;
}
稍后这将被释放到顶部。在这里,您释放 records
的元素以及 分配给它们的指针集。
for (int i = 0; i < entryCount; i++) {
free(records[i]->fullPath);
free(records[i]);
}
free(records);
如果在一次迭代中计数不为零,我们会为记录分配更多 space。然后,循环 tmpRecords
并将 元素 分配给 records
。当 records
被释放时,这些元素将被释放,但是为 tmpRecords
分配的指针集本身永远不会被释放。我在下面的代码中添加了一行来修复此泄漏。
if (*count == 0) {
*records = tmpRecords;
}
else {
struct record** newRecords = realloc(*records, newCount * sizeof *newRecords);
if (!newRecords) {
printfColor(C_RED, "Couldn't allocate memory for further records\n");
*error = true;
goto cleanup;
}
*records = newRecords;
for (int i = 0, j = *count; i < processedFiles; i++, j++) {
(*records)[j] = tmpRecords[i];
}
free(tmpRecords); /*** THIS LINE ***/
}
我的小 C 程序正在遍历工作目录的内容,这没有问题,但我用 Valgrind 检查了它,发现有一些神秘的漏洞,我好几天都无法消除。
代码如下:
struct record {
char* fullPath;
int wdlen; // working directory length
int relplen; // relative path length
int namlen; // name length
bool folder; // is folder
long modified;
};
void populateDB() {
struct record** records = NULL;
int entryCount = 0;
bool error = false;
listDirRecursive(workingDirectory, &entryCount, &records, &error);
if (error) {
goto cleanup;
}
for (int i = 0; i < entryCount; i++) {
printfColor(C_MAGENTA, "%d. wdlen: %d,\t\trelPathLen: %d,\t\tnamelen: %d,\t\tfolder: %s,\t\tmod: %ld,\t\tpath: %s\n",
i, records[i]->wdlen, records[i]->relplen, records[i]->namlen, records[i]->folder ? "true" : "false", records[i]->modified, records[i]->fullPath);
}
// initDatabase(&entryCount, &records);
cleanup:
for (int i = 0; i < entryCount; i++) {
free(records[i]->fullPath);
free(records[i]);
}
free(records);
}
void listDirRecursive(const char* path, int* count, struct record*** records, bool* error) {
if (*error) return;
struct dirent** fileList = NULL;
int fileCount = scandir(path, &fileList, NULL, alphasort);
if (fileCount < 0) {
printfColor(C_RED, "Couldn't open: %s\n", path);
*error = true;
return;
}
struct record** tmpRecords = malloc(fileCount * sizeof *tmpRecords);
if (!tmpRecords) {
printfColor(C_RED, "Couldn't allocate memory for records\n");
*error = true;
return;
}
struct record* act = NULL;
struct stat filestat;
int processedFiles = 0;
for (int i = 0, wdlen = strlen(workingDirectory), plen = strlen(path); i < fileCount; i++) {
if (strcmp(fileList[i]->d_name, ".") == 0 ||
strcmp(fileList[i]->d_name, "..") == 0) continue;
act = malloc(sizeof *act);
if (!act) {
printfColor(C_RED, "Couldn't allocate memory for actual record\n");
*error = true;
goto cleanup;
}
act->fullPath = malloc(plen + fileList[i]->d_namlen + 2);
if (!act->fullPath) {
printfColor(C_RED, "Couldn't allocate memory for actual record's full path\n");
*error = true;
goto cleanup;
}
strcat(strcat(strcpy(act->fullPath, path), "/"), fileList[i]->d_name);
act->wdlen = wdlen + 1;
act->relplen = strlen(act->fullPath) - ((wdlen + 1) + fileList[i]->d_namlen);
act->namlen = fileList[i]->d_namlen;
act->folder = fileList[i]->d_type == DT_DIR;
stat(act->fullPath, &filestat);
act->modified = filestat.st_mtimespec.tv_nsec;
tmpRecords[processedFiles++] = act;
if (fileList[i]->d_type == DT_DIR) {
char* p = malloc(plen + fileList[i]->d_namlen + 2);
strcat(strcat(strcpy(p, path), "/"), fileList[i]->d_name);
listDirRecursive(p, count, records, error);
free(p);
}
}
if (processedFiles == 0) {
goto cleanup;
}
struct record** shrinkedTmpRecords = realloc(tmpRecords, processedFiles * sizeof *shrinkedTmpRecords);
if (!shrinkedTmpRecords) {
if (verbose) printf("Shrinking tmpRecords failed. Some memory leak will happen\n");
}
else {
tmpRecords = shrinkedTmpRecords;
}
int newCount = *count + processedFiles;
if (*count == 0) {
*records = tmpRecords;
}
else {
struct record** newRecords = realloc(*records, newCount * sizeof *newRecords);
if (!newRecords) {
printfColor(C_RED, "Couldn't allocate memory for further records\n");
*error = true;
goto cleanup;
}
*records = newRecords;
for (int i = 0, j = *count; i < processedFiles; i++, j++) {
(*records)[j] = tmpRecords[i];
}
}
*count = newCount;
cleanup:
for (int i = 0; i < fileCount; i++) {
free(fileList[i]);
}
free(fileList);
}
在运行之后,Valgrind的输出是这样的:
==40861== HEAP SUMMARY:
==40861== in use at exit: 18,084 bytes in 170 blocks
==40861== total heap usage: 423 allocs, 253 frees, 180,357 bytes allocated
==40861==
==40861== 8 bytes in 1 blocks are definitely lost in loss record 1 of 43
==40861== at 0x100111FD6: realloc (in /usr/local/Cellar/valgrind/HEAD-e0af3eb/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==40861== by 0x10000162B: listDirRecursive (filesystem.c:137)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x100001092: populateDB (filesystem.c:44)
==40861== by 0x100000E15: init (mtl.c:111)
==40861== by 0x100000CD2: main (mtl.c:61)
==40861==
==40861== 16 bytes in 2 blocks are definitely lost in loss record 5 of 43
==40861== at 0x100111FD6: realloc (in /usr/local/Cellar/valgrind/HEAD-e0af3eb/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==40861== by 0x10000162B: listDirRecursive (filesystem.c:137)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x100001092: populateDB (filesystem.c:44)
==40861== by 0x100000E15: init (mtl.c:111)
==40861== by 0x100000CD2: main (mtl.c:61)
==40861==
==40861== 72 bytes in 5 blocks are definitely lost in loss record 22 of 43
==40861== at 0x100111FD6: realloc (in /usr/local/Cellar/valgrind/HEAD-e0af3eb/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==40861== by 0x10000162B: listDirRecursive (filesystem.c:137)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x100001092: populateDB (filesystem.c:44)
==40861== by 0x100000E15: init (mtl.c:111)
==40861== by 0x100000CD2: main (mtl.c:61)
==40861==
==40861== 80 bytes in 1 blocks are definitely lost in loss record 23 of 43
==40861== at 0x100111FD6: realloc (in /usr/local/Cellar/valgrind/HEAD-e0af3eb/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==40861== by 0x10000162B: listDirRecursive (filesystem.c:137)
==40861== by 0x100001092: populateDB (filesystem.c:44)
==40861== by 0x100000E15: init (mtl.c:111)
==40861== by 0x100000CD2: main (mtl.c:61)
==40861==
==40861== 112 bytes in 4 blocks are definitely lost in loss record 26 of 43
==40861== at 0x100111FD6: realloc (in /usr/local/Cellar/valgrind/HEAD-e0af3eb/lib/valgrind/vgpreload_memcheck-amd64-darwin.so)
==40861== by 0x10000162B: listDirRecursive (filesystem.c:137)
==40861== by 0x1000015DA: listDirRecursive (filesystem.c:127)
==40861== by 0x100001092: populateDB (filesystem.c:44)
==40861== by 0x100000E15: init (mtl.c:111)
==40861== by 0x100000CD2: main (mtl.c:61)
==40861==
==40861== LEAK SUMMARY:
==40861== definitely lost: 288 bytes in 13 blocks
==40861== indirectly lost: 0 bytes in 0 blocks
==40861== possibly lost: 0 bytes in 0 blocks
==40861== still reachable: 0 bytes in 0 blocks
==40861== suppressed: 17,796 bytes in 157 blocks
288字节除以8(指针的大小)为36,即末尾的总记录(文件和文件夹)。 13 个块数等于文件夹数。
我尝试了我想到的所有方法,但没有任何效果,我正在学习 C,所以我可能不知道一些重要的东西(我在主题中阅读了很多并试图避免大否-编号).
如果有人不明白那是什么 printfColor
,这里是:
// header file:
enum Color {
C_RED,
C_BOLD_RED,
C_GREEN,
C_BOLD_GREEN,
C_YELLOW,
C_BOLD_YELLOW,
C_BLUE,
C_BOLD_BLUE,
C_MAGENTA,
C_BOLD_MAGENTA,
C_CYAN,
C_BOLD_CYAN,
C_RESET
};
static const char* colors[] = {
"3[0;31m", // RED
"3[1;31m", // BOLD RED
"3[0;32m", // GREEN
"3[1;32m", // BOLD GREEN
"3[0;33m", // YELLOW
"3[01;33m", // BOLD YELLOW
"3[0;34m", // BLUE
"3[1;34m", // BOLD BLUE
"3[0;35m", // MAGENTA
"3[1;35m", // BOLD MAGENTA
"3[0;36m", // CYAN
"3[1;36m", // BOLD CYAN
"3[0m" // RESET
};
// c file:
void printfColor(enum Color color, const char* format, ...) {
va_list args;
printf("%s", colors[color]);
va_start(args, format);
vprintf(format, args);
va_end(args);
printf("%s", colors[C_RESET]);
}
干杯
Shawn 的评论是正确的。 tmpRecords 被分配并用作一组指针:
struct record** shrinkedTmpRecords = realloc(tmpRecords, processedFiles * sizeof *shrinkedTmpRecords);
if (!shrinkedTmpRecords) {
if (verbose) printf("Shrinking tmpRecords failed. Some memory leak will happen\n");
}
else {
tmpRecords = shrinkedTmpRecords;
}
然后,如果计数为 0,您只是将 records
分配给 tmpRecords
:
if (*count == 0) {
*records = tmpRecords;
}
稍后这将被释放到顶部。在这里,您释放 records
的元素以及 分配给它们的指针集。
for (int i = 0; i < entryCount; i++) {
free(records[i]->fullPath);
free(records[i]);
}
free(records);
如果在一次迭代中计数不为零,我们会为记录分配更多 space。然后,循环 tmpRecords
并将 元素 分配给 records
。当 records
被释放时,这些元素将被释放,但是为 tmpRecords
分配的指针集本身永远不会被释放。我在下面的代码中添加了一行来修复此泄漏。
if (*count == 0) {
*records = tmpRecords;
}
else {
struct record** newRecords = realloc(*records, newCount * sizeof *newRecords);
if (!newRecords) {
printfColor(C_RED, "Couldn't allocate memory for further records\n");
*error = true;
goto cleanup;
}
*records = newRecords;
for (int i = 0, j = *count; i < processedFiles; i++, j++) {
(*records)[j] = tmpRecords[i];
}
free(tmpRecords); /*** THIS LINE ***/
}