【问题标题】:How to properly use scandir() in c?如何在c中正确使用scandir()?
【发布时间】:2013-08-26 11:33:11
【问题描述】:

我正在尝试将文件列表存储在 char** 变量中。

scandir() 正确完成,但在尝试打印 char** 时出现分段错误。

代码如下:

int main()
{
    char** fileList;
    int noOfFiles;
    char* path = ".";
    makeList(&fileList, &noOfFiles, path); 
    return 0;
}

void makeList(char ***fileList, int* noOfFiles, char* path){
    struct dirent **fileListTemp;
    *noOfFiles = scandir(path, &fileListTemp, NULL, alphasort);
    int i;
    fileList = (char***)malloc(sizeof(char***));
    *fileList = (char**)malloc(*noOfFiles * sizeof(char*));
    printf("total: %d files\n",*noOfFiles);
    for(i = 0; i < *noOfFiles; i++){
        *fileList[i] = (char*)malloc(strlen(fileListTemp[i] -> d_name) *sizeof(char));
        strcpy(*fileList[i], fileListTemp[i] -> d_name);
        printf("%s\n",*fileList[i]);
    }
    return;
}

这会在打印 2 个文件名后出现分段错误。

输出:

总共:27 个文件。

..

.j.v

分段错误(核心转储)

【问题讨论】:

  • 不需要强制转换分配的内存?请详细说明。
  • 那个链接有一个很好的解释。关于您的问题,您传入char ***fileList(指向已声明变量的指针,即main 中的char **fileList),然后直接写入堆栈上的值。你的意思是说*fileList = malloc(sizeof(char**))?对不同级别的指针使用相同的名称会很快造成混淆。
  • *fileList = malloc(sizeof(char**)) 实际上是的。但是我不需要指定否。这将包含的字符串?我知道每个指针的深处都只存储一个内存位置,但没有字符串必须存储在某个地方......?
  • 在您的作业到期之前,我无法为您提供帮助,抱歉。我有一些建议: (1) 对指针的“级别”使用不同的名称(不要声明int *i,然后在签名中使用int **i); (2) 为了清楚起见,考虑makeList 中的一个临时变量来保存新分配的指针; (3)在纸上用方框和箭头勾勒出你的每一项作业都应该用来确保你正确间接的地方; (4) 使用简化 C 约定,例如不强制转换 malloc 并且从不说 sizeof(char)(根据定义始终为 1)。

标签: c linux string malloc scandir


【解决方案1】:

函数scandir()为你分配内存。

您不需要分配任何内存。你确实需要释放scandir()返回给你的内存。

您的代码调用:*noOfFiles = scandir(path, &amp;fileListTemp, NULL, alphasort);

返回时,noOfFiles 将包含path 目录中的目录条目数,fileListTemp 将指向已分配的指针数组,该数组指向已分配的 struct dirent blob,每个 blob 都有一个 d_name 成员,该成员指向文件/目录的以 null 结尾的名称。

如果您的目录包含文件“FirstFile.txt”、“AnotherFile.txt”、“ThirdFile.txt”,例如,在您的调用中,从scandir() 返回时,noOfFiles 将设置为 5 三个文件加上两个“。”和“..”目录条目。如果您未通过“alphasort”,则参赛作品将不按特定顺序排列。 (实际上这有点不正确。它们将按目录文件名条目的顺序排列,这取决于最初创建文件的顺序。)

因为您传递了“alphasort”,所以您应该按以下顺序查看条目(我明确显示了 null-byte-string-terminator:

fileListTemp[0]->d_name == ".\0"
fileListTemp[1]->d_name == "..\0"
fileListTemp[2]->d_name == "AnotherFile.txt\0"
fileListTemp[3]->d_name == "FirstFile.txt\0"
fileListTemp[4]->d_name == "ThirdFile.txt\0"

所以 fileListTemp 指向一个分配的内存块,其中包含五个 struct dirent 指针。五个 struct dirent 指针中的每一个都指向一个 struct dirent 已分配内存块,其中包含一个以空值结尾的目录d_name 成员中的条目名称。 (即使这样也算是简化了,因为d_name entry也是一个指针,但是它指向的是分配块尾部的额外分配空间,而entry name就存放在那里。)

块已分配的内存。

你可以使用这个分配的内存,直到你用完它,然后你在数组中的每个条目上调用 free(),然后是数组本身的 free()。

您必须释放每个条目以及数组本身。它们都是独立分配的内存块。

完成列表后,您应该:

for (int i = 0; i < noOfFiles; i++)
  {
  free(fileListTemp[i];
  }

free(fileListTemp);

【讨论】:

    【解决方案2】:

    这是一个老问题,但由于我遇到了它,但它并没有像手册页那样有效地解决我的问题,所以我从man 页面复制了一个代码 sn-p 作为新的答案未来。

      #include <dirent.h>
    
       int
       main(void)
       {
           struct dirent **namelist;
           int n;
    
           n = scandir(".", &namelist, NULL, alphasort);
           if (n < 0)
               perror("scandir");
           else {
               while (n--) {
                   printf("%s\n", namelist[n]->d_name);
                   free(namelist[n]);
               }
               free(namelist);
           }
       }
    

    【讨论】:

    • 这个不会编译,把这个加到最上面#include &lt;stdio.h&gt;#include &lt;stdlib.h&gt;
    • @gschaden,如果您查看了关于 SO 的足够多的答案,您会发现大多数使用标准标题的人都假设它们。
    【解决方案3】:

    修改代码,可以了!

      #include <stdio.h>
      #include <sys/types.h>
      #include <sys/dir.h>
      #include <stdlib.h>
      #include <string.h>
    
      void makeList(char ***fileList, int* noOfFiles, char* path){
          struct dirent **fileListTemp;
          *noOfFiles = scandir(path, &fileListTemp, NULL, alphasort);
          int i;
          *fileList = (char**)malloc(*noOfFiles * sizeof(char*));
          printf("total: %d files\n",*noOfFiles);
          for(i = 0; i < *noOfFiles; i++){
              (*fileList)[i] = (char*)malloc(strlen(fileListTemp[i] -> d_name)+1);
              strcpy((*fileList)[i], fileListTemp[i] -> d_name);
              printf("%s\n",(*fileList)[i]);
          }   
          return;
      }
    
      int main()
      {
          char** fileList;
          int noOfFiles;
          char* path = ".";
          makeList(&fileList, &noOfFiles, path); 
          return 0;
      }
    

    fileListchar *** 的类型,所以*fileList 是主函数中的fileList 变量。

    *fileList = (char**)malloc(*noOfFiles * sizeof(char*));
    

    使用此语句,*fileList 指向已分配的指针数组内存。
    如果要为数组中的每个指针分配内存,我们需要使用(*fileList)[i],除了*fileList[i][]的优先级高于*

    【讨论】:

    • 您的 fileList 构造是正确的,并且正确地从 dirent 对象中提取了字符串,不幸的是它泄漏了内存,因为 fileListTemp 指向的内存块从未被释放。如果 makeList() 被调用了很多次,例如通过类似于 file-tree-walker 的函数 -- linux.die.net/man/3/nftw -- 每次调用都会从堆中泄漏内存。是的,当应用程序在现代操作系统中退出时,内存会被恢复,但如果调用次数足够多,它仍然会使应用程序崩溃,并且对于嵌入式系统,您的应用程序通常只会在电源循环时“退出”。
    【解决方案4】:

    我不确定这是不是问题,但您必须为空终止分配另一个字节:

    *fileList[i] = (char*)malloc(strlen(fileListTemp[i] -> d_name + 1) *sizeof(char));
    

    【讨论】:

    • 这里有很多指针意大利面。其中大部分是不可避免的,但我怀疑堆栈粉碎。
    • 这就是挑战...否则我可以想出其他更长的方法来获得相同的结果...但是我还是个学习者...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-07-16
    • 2020-08-26
    • 1970-01-01
    • 2015-07-12
    • 2013-04-10
    相关资源
    最近更新 更多