我有一个用于遍历目录的基本递归函数。
代码中存在一些内存泄漏。但是我找不到它们,我在某些行中尝试了free()
,但是它不起作用。
有6个alloc和4个free。我怎样才能使它6个分配和6个免费?我认为我应该free
depthPath
,不是吗? (如果是,我应该free
在哪里?)
这是我的c程序:
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
void recursiveFoo (char *path);
int main(int argc, char **argv){
recursiveFoo (argv[1]);
return 0;
}
void recursiveFoo (char *path){
char *depthPath;
DIR *d;
struct dirent *dir;
d = opendir(path);
if (d) {
while ((dir = readdir(d)) != NULL) {
depthPath= (char*)malloc(strlen(path) + strlen(dir->d_name) + 1);
strcpy(depthPath, path);
strcat(depthPath, "/");
strcat(depthPath, dir->d_name);
if(((strcmp(dir->d_name,".")!=0) && (strcmp(dir->d_name,".."))!=0) ){
recursiveFoo(depthPath);
free(depthPath);
}
}
printf("%s/",path );
closedir(d);
}
}
用-ggdb3
编译后,这是我的Valgrind输出:
==641== HEAP SUMMARY:
==641== in use at exit: 13 bytes in 2 blocks
==641== total heap usage: 6 allocs, 4 frees, 33,876 bytes allocated
==641==
==641== Searching for pointers to 2 not-freed blocks
==641== Checked 62,760 bytes
==641==
==641== 13 bytes in 2 blocks are definitely lost in loss record 1 of 1
==641== at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==641== by 0x400839: recursiveFoo (stack.c:29)
==641== by 0x4007D7: main (stack.c:14)
==641==
==641== LEAK SUMMARY:
==641== definitely lost: 13 bytes in 2 blocks
==641== indirectly lost: 0 bytes in 0 blocks
==641== possibly lost: 0 bytes in 0 blocks
==641== still reachable: 0 bytes in 0 blocks
==641== suppressed: 0 bytes in 0 blocks
==641==
==641== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
==641== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)
答案 0 :(得分:1)
您没有为depthPath
分配足够的内存:
depthPath= (char*)malloc(strlen(path) + strlen(dir->d_name) + 1);
strcpy(depthPath, path);
strcat(depthPath, "/");
strcat(depthPath, dir->d_name);
生成的字符串需要在结尾的\0
处有一个额外的字节。
要解决内存泄漏问题,可以在进行下一次迭代之前将free(depthPath);
添加到循环的底部。
或者,您可以修改代码以使用realloc()
而不是malloc()
,然后在循环完成后调用free()
。尝试使用此方法时,您可能希望将depthPath
初始化为NULL
。
当您递归到目录树中时,该算法分配 O(n 2 )内存。为了避免这种情况,您可以修改方法,以便将path
参数本身传递给 realloc()以便为其他子目录腾出空间。但是,由于 realloc()返回的指针值可能不同于传递给它的指针值,因此该指针值需要返回给调用方。
要保持最初的API使用,可以将具有realloc()
调用的实际递归函数封装到一个辅助函数中。
static void recursiveFoo__(char **, int);
void recursiveFoo (const char *path) {
int depthPathLen = strlen(path);
char *depthPath = malloc(depthPathLen+1);
strcpy(depthPath, path);
recursiveFoo__(&depthPath, depthPathLen);
free(depthPath);
}
void recursiveFoo__ (char **depthPath, int depthPathLen){
DIR *d;
struct dirent *dir;
d = opendir(*depthPath);
if (d) {
while ((dir = readdir(d)) != NULL) {
int newPathLen = depthPathLen + strlen(dir->d_name) + 1;
char *newPath= realloc(*depthPath, newPathLen+1);
if (newPath == NULL) {
fprintf(stderr, "oops: %.*s/%s\n",
depthPathLen, *depthPath, dir->d_name);
break;
}
*depthPath = newPath;
strcpy(newPath+depthPathLen, "/");
strcpy(newPath+depthPathLen+1, dir->d_name);
if(((strcmp(dir->d_name,".")!=0) &&
(strcmp(dir->d_name,".."))!=0) ){
recursiveFoo__(depthPath, newPathLen);
}
}
printf("%.*s/\n", depthPathLen, *depthPath);
closedir(d);
}
}
现在,内存分配将为 O(n)。
答案 1 :(得分:1)
此代码没有为复制到块中的字符串分配足够的内存:
depthPath= (char*)malloc(strlen(path) + strlen(dir->d_name) + 1);
strcpy(depthPath, path);
strcat(depthPath, "/");
strcat(depthPath, dir->d_name);
新字符串需要strlen(path) + strlen(dir->d_name) + strlen( "/" ) + 1
个字符来说明"/"
和终止的'\0'
。
另外,为了解决您的漏洞,此代码
while ((dir = readdir(d)) != NULL) {
depthPath= (char*)malloc(strlen(path) + strlen(dir->d_name) + 1);
strcpy(depthPath, path);
strcat(depthPath, "/");
strcat(depthPath, dir->d_name);
if(((strcmp(dir->d_name,".")!=0) && (strcmp(dir->d_name,".."))!=0) ){
recursiveFoo(depthPath);
}
}
可以写为
while ((dir = readdir(d)) != NULL)
{
if(((strcmp(dir->d_name,".")==0) ||
(strcmp(dir->d_name,".."))==0) )
{
continue;
}
char *depthPath= malloc(strlen(path) + strlen(dir->d_name) + 2);
strcpy(depthPath, path);
strcat(depthPath, "/");
strcat(depthPath, dir->d_name);
recursiveFoo(depthPath);
free(depthPath):
}