今天看了《Linux高级程序设计》这本书,在第120页有一个关于readdir_r函数的例子,如下:

int main()
     {
         DIR *dirp;
         struct dirent *dp1 = malloc(sizeof(struct dirent));
         struct dirent *dp2 = malloc(sizeof(struct dirent));
         dirp = opendir(".");
         while(1)
         {
             if ((readdir_r(dirp, dp1, &dp2)) != 0)
             {
                 break;
             }
             if (dp2==NULL)
             {
                 break;
             }
             if (dp2->d_name[0] == '.')
             {
                 break;
             }
             printf("inode=%d\t", dp2->d_ino);
             printf("reclen=%d\t", dp2->d_reclen);
             printf("name=%s\n", dp2->d_name);
         }
         closedir(dirp);
         free(dp1);
         free(dp2);
         return 0;
     }


    可能是作者在使用这个例子时并没有过多的考虑它的正确性和严谨性,造成该例子有几个错误或不严谨的地方,分析如下:
    第一,在调用opendir打开当前目录时,并没有判断返回值是否可用就直接调用readdir_r了,尽管大多时候打开当前目录总是成功的,但这并不严谨,假如函数参数不是固定的,而是用户指定的,那么我们无法保证该目录能够正确打开,所以必须要判断这个打开操作是否成功,如果失败,则需要提示用户。
    第二,调用readdir_r时,如果读到结束,则会设置dp2为NULL,而dp2是通过malloc分配的,所以一旦设置为NULL后,就无法再释放这个内存空间了,尽管通过free释放该指针时不会报错,但是这会造成内存泄露,是一个比较严重的问题。可能有人觉得main函数退出后系统会自动释放这些已分配的内存空间,但是从程序员的角度来说,这就是一个很容易犯但是又必须避免的错误。
    第三,readdir_r的第三个参数,通过传入一个指针的指针来返回一个dirent指针,其实这个指针是不需要我们分配空间的,只需要指定一个指针,然后传入该指针的地址,那么该指针就指向了函数返回的dirent结构,如果函数失败,则会将该指针设置为NULL,即这个返回结构指针是由readdir_r函数为我们分配的,而不是在调用之前使用malloc或其他函数分配的内存,且不可使用free函数去释放这个分配值。
    第四,readdir_r的第二个参数,也没有必要通过malloc在堆上分配空间,只需要定义一个dirent变量,并传入这个变量的指针即可。这样做的好处是,变量分配在栈空间上的效率要比分配在堆空间上的效率高,且避免了忘记释放内存的错误。
    修改以上四个问题后,重新编写的程序如下:

int main()
     {
         struct dirent dp1 = {0};
         struct dirent *dp2 = NULL;
         DIR *dirp = opendir(".");
         if (dirp == NULL)
         {
             printf("open dir error: %d\n", errno);
             return -1;
         }        while(1)
         {
             if ((readdir_r(dirp, &dp1, &dp2)) != 0)
             {
                 break;
             }
             if (dp2==NULL)
             {
                 break;
             }
             if (dp2->d_name[0] == '.')
             {
                 break;
             }
             printf("inode=%d\t", dp2->d_ino);
             printf("reclen=%d\t", dp2->d_reclen);
             printf("name=%s\n", dp2->d_name);
         }
         closedir(dirp);
         return 0;
     }


  

https://blog.51cto.com/jslmes/1183200