在C的递归函数中使用free()函数

时间:2019-03-08 22:41:55

标签: c recursion

我有一个用于遍历目录的基本递归函数。

代码中存在一些内存泄漏。但是我找不到它们,我在某些行中尝试了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)

2 个答案:

答案 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):
    }