通过递归函数传递已分配的数组时内存泄漏

时间:2017-05-27 13:25:36

标签: c memory-leaks malloc valgrind

这是函数list_directory,我失去了分配的指针,我以后无法释放它。 这应该是类似ls-a的实现,当它找到一个目录时,它应该保存它的名字,在列出目录后,它应该递归地在当前目录中找到的目录上调用list_directory。但由于某种原因,它在途中失去了一些元素。

int list_directory(int argc, char *argv[], struct check_info *chinfo, int dirs)
{
    struct group *grp;
    struct passwd *pwd;
    struct stat file_info;
    struct tm *mtime;
    struct dirent *dir_info;

    int subdirs = 0;
    int printsubdir = 0;
    int skip_newline = 1;

    char timebuffer[26];
    char **recursqueue;

    if (chinfo->param_R)
    {
        if ((recursqueue = malloc(argc * sizeof(char*))) < 0)
            perror("malloc");
    }

    if (dirs > -1)
    {
        for (int i = 0; i < chinfo->files; i++)
        {
            list_file(chinfo->argv_files[i], chinfo);
            free(chinfo->argv_files[i]);
        }
        free(chinfo->argv_files);
    }

    for (int i = 1; i < argc; i++)
    {
        DIR *dp = opendir(argv[i]);

        if (dp)
        {
            if (chinfo->param_R)
            {
                if ((recursqueue[subdirs] = malloc((256) * sizeof(char))) < 0)
                    perror("malloc");
                strcpy(recursqueue[subdirs++], "./ls");
            }

            if (dirs+chinfo->files > 1 || chinfo->param_R)
            {
                if (!skip_newline || dirs == -1 || chinfo->files)
                    printf("\n%s:\n", argv[i]);
                else
                    printf("%s:\n", argv[i]);
            }

            skip_newline = 0;

            if (chinfo->param_l)
            {
                dir_indent_info(argv[i], chinfo);
                printf("total %lu\n", chinfo->blocks_total/2);

            }

            while ((dir_info = readdir(dp)) != NULL)
            {
                if (!strcmp(".", dir_info->d_name) ||
                    !strcmp("..", dir_info->d_name))
                {
                    continue;
                }
                else if (!strncmp(".", dir_info->d_name, 1)
                         && !chinfo->param_A)
                {
                    continue;
                }
                else
                {
                    char path[strlen(argv[i]) + strlen(dir_info->d_name) + 1];
                    sprintf(path, "%s/%s", argv[i], dir_info->d_name);

                    if (lstat(path, &file_info) == -1)
                        continue;

                    switch (file_info.st_mode & S_IFMT)
                    {
                        case S_IFBLK:  printf("b"); break;
                        case S_IFCHR:  printf("c"); break;
                        case S_IFDIR:  printf("d");
                        if (chinfo->param_R)
                        {
                            printsubdir = 1;
                            recursqueue[subdirs] = malloc((256) * sizeof(char));
                            strcpy(recursqueue[subdirs++], path);
                        }
                        break;
                        case S_IFIFO:  printf("p"); break;
                        case S_IFLNK:  printf("l"); break;
                        case S_IFREG:  printf("-"); break;
                        case S_IFSOCK: printf("s"); break;
                        default:       printf("?"); break;
                    }

                    if (chinfo->param_l)
                    {
                        printf( (file_info.st_mode & S_IRUSR) ? "r" : "-");
                        printf( (file_info.st_mode & S_IWUSR) ? "w" : "-");
                        printf( (file_info.st_mode & S_IXUSR) ? "x" : "-");
                        printf( (file_info.st_mode & S_IRGRP) ? "r" : "-");
                        printf( (file_info.st_mode & S_IWGRP) ? "w" : "-");
                        printf( (file_info.st_mode & S_IXGRP) ? "x" : "-");
                        printf( (file_info.st_mode & S_IROTH) ? "r" : "-");
                        printf( (file_info.st_mode & S_IWOTH) ? "w" : "-");
                        printf( (file_info.st_mode & S_IXOTH) ? "x" : "-");
                        printf(" %*lu", numlen(chinfo->link_len), file_info.st_nlink);

                        pwd = getpwuid(file_info.st_uid);
                        printf(" %-*s", chinfo->usr_len, pwd->pw_name);

                        grp = getgrgid(file_info.st_gid);
                        printf(" %-*s", chinfo->grp_len, grp->gr_name);

                        printf(" %*lu", numlen(chinfo->size_len), file_info.st_size);
                        mtime = localtime(&file_info.st_mtime);
                        strftime(timebuffer, 26, "%b %e %R", mtime);
                        printf(" %s", timebuffer);

                    }

                    printf(" %s\n", dir_info->d_name);
                }
            }
            if (printsubdir)
            {
                if((list_directory(subdirs, recursqueue, chinfo, -1)) < 0)
                    printsubdir = -1;

                for (int i = 0; i < subdirs; i++)
                {
                    free(recursqueue[i]);
                }
                if (printsubdir < 0)
                    return -1;
            }
            closedir (dp);
            subdirs = 0;
        }
    }
    if (chinfo->param_R)
        free(recursqueue);
    return 0;
}

我将argv传递给dir_indent_info,所以这里是:

int dir_indent_info(char* dirpath, struct check_info *chinfo)
{
    struct dirent *dir_info;
    struct stat file_info;
    struct group *grp;
    struct passwd *pwd;

    reset_info(chinfo);

    DIR *dp = opendir(dirpath);
    if (dp)
    {
        while ((dir_info = readdir(dp)) != NULL)
        {
            if (!strcmp(".", dir_info->d_name) ||
                !strcmp("..", dir_info->d_name))
            {
                continue;
            }

            char path[strlen(dir_info->d_name) + strlen(dirpath) + 1];

            sprintf(path, "%s/%s", dirpath, dir_info->d_name);

            if (lstat(path, &file_info) == -1)
            {
                perror("lstat()");
                continue;
            }

            pwd = getpwuid(file_info.st_uid);
            if (strlen(pwd->pw_name) > chinfo->usr_len)
                chinfo->usr_len = strlen(pwd->pw_name);

            grp = getgrgid(file_info.st_gid);
            if (strlen(grp->gr_name) > chinfo->grp_len)
                chinfo->grp_len = strlen(grp->gr_name);

            chinfo->blocks_total += file_info.st_blocks;

            if (file_info.st_size > chinfo->size_len)
                chinfo->size_len = file_info.st_size;

            if (file_info.st_nlink > chinfo->link_len)
                chinfo->link_len = file_info.st_nlink;
        }
        closedir(dp);
    }
    else
    {
        perror("error");
        return -1;
    }
    return 0;
}

Valgrind输出:

==6361== WARNING: new redirection conflicts with existing -- ignoring it
--6361--     old: 0x0401cdc0 (strlen              ) R-> (0000.0) 0x3809e181 ???
--6361--     new: 0x0401cdc0 (strlen              ) R-> (2007.0) 0x04c31020 strlen


==6361== 
==6361== HEAP SUMMARY:
==6361==     in use at exit: 768 bytes in 3 blocks
==6361==   total heap usage: 231 allocs, 228 frees, 596,108 bytes allocated
==6361== 
==6361== Searching for pointers to 3 not-freed blocks
==6361== Checked 65,288 bytes
==6361== 
==6361== 256 bytes in 1 blocks are definitely lost in loss record 1 of 2
==6361==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6361==    by 0x4016FD: list_directory (ls.c:293)
==6361==    by 0x401DFB: list_directory (ls.c:385)
==6361==    by 0x401F55: main (ls.c:415)
==6361== 
==6361== 512 bytes in 2 blocks are definitely lost in loss record 2 of 2
==6361==    at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
==6361==    by 0x4016FD: list_directory (ls.c:293)
==6361==    by 0x401DFB: list_directory (ls.c:385)
==6361==    by 0x401DFB: list_directory (ls.c:385)
==6361==    by 0x401F55: main (ls.c:415)
==6361== 
==6361== LEAK SUMMARY:
==6361==    definitely lost: 768 bytes in 3 blocks
==6361==    indirectly lost: 0 bytes in 0 blocks
==6361==      possibly lost: 0 bytes in 0 blocks
==6361==    still reachable: 0 bytes in 0 blocks
==6361==         suppressed: 0 bytes in 0 blocks
==6361== 
==6361== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 1 from 1)

为char **数组分配指针:

if (chinfo->param_R)
{
     if ((recursqueue = malloc(argc * sizeof(char*))) < 0)
         perror("malloc");
}

分配第一个元素并为其分配./ls以模拟参数输入:

if (chinfo->param_R)
{
     if ((recursqueue[subdirs] = malloc((256) * sizeof(char))) < 0)
         perror("malloc");
     strcpy(recursqueue[subdirs++], "./ls");
}

如果当前文件是目录,我们将其添加到recursqueue

case S_IFDIR:  printf("d");
     if (chinfo->param_R)
     {
          printsubdir = 1;
          recursqueue[subdirs] = malloc((256) * sizeof(char));
          strcpy(recursqueue[subdirs++], path);
     }

使用收集的子目录调用相同的函数&#39;路径:

if (printsubdir)
{
    if((list_directory(subdirs, recursqueue, chinfo, -1)) < 0)
        printsubdir = -1;

    for (int i = 0; i < subdirs; i++)
    {
        free(recursqueue[i]);
    }
    if (printsubdir < 0)
         return -1;
}

1 个答案:

答案 0 :(得分:0)

您的代码存在许多问题,例如巨大的函数,未初始化的变量,变量的时尚名称,不相关的变量重用,变量的不必要的广泛范围,分配的内存的模糊所有权,条件表达式中的赋值,小于指针与0等的比较

至于内存泄漏,这些代码很容易发生,例如:

  • 您为recursqueue字符串分配了第一个./ls项,但如果printsubdir未设置为1,那么它将永远不会被释放;
  • 您分配recursqueue数组,但如果list_directory返回小于0的值,则永远不会释放它(并且也不会调用closedir);