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

问题描述:

这是函数list_directory,其中我丢失了分配的指针,之后我无法释放它。 这应该是一个类似于实现的方式,当它找到一个目录时,它应该保存它的名称,并且在列出该目录后,它应该递归地在当前目录中找到的目录上调用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; 
} 

我通过argvdir_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) 

分配指针**阵列:

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

分配的第一要素和分配./ls它来模拟参数输入:如果当前文件是我们在recursqueue

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

添加一个目录调用具有相同功能的

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

收集到的子目录路径:

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; 
} 
+0

请问您可以发布精确的函数(或者只是相关的部分,分配和递归发生的地方)。这个问题真的很难。 –

+0

@AjayBrahmakshatriya现在好吗? –

+0

你从malloc返回的检查应该与0比较。我期望一个指针等于一个无符号或无符号的long(就像size_t),因此我不希望有可能返回一个负值。通常返回NULL/0是错误条件。 –

你的代码有许多问题,如庞大的功能,未初始化变量,变量名时尚,再利用无关的目的变量,变量的不必要的广泛范围,模糊的所有权分配的内存,在条件表达式中的分配,小于0的指针比较等。

至于内存泄漏,它们很容易发生这样的代码,例如:

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

我发现我的错误,但感谢您的答案,并指出在这个混乱中必须改进的东西。 –