免费注册 查看新帖 |

Chinaunix

  平台 论坛 博客 文库
最近访问板块 发新帖
查看: 3334 | 回复: 1
打印 上一主题 下一主题

glibc中scandir函数源码的疑问 [复制链接]

论坛徽章:
0
跳转到指定楼层
1 [收藏(0)] [报告]
发表于 2010-03-23 00:11 |只看该作者 |倒序浏览
本帖最后由 changyongID 于 2010-03-23 00:13 编辑

贴上代码之后,贴子显得比较长,但问题并不复杂。请各位看一下,问题描述如下


函数scandir一般是用来对指定目录排序中的。其在glibc中源码如下:
  1. #include <dirent.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #include <errno.h>
  5. #include <bits/libc-lock.h>

  6. #ifndef SCANDIR
  7. #define SCANDIR scandir
  8. #define READDIR __readdir
  9. #define DIRENT_TYPE struct dirent
  10. #endif

  11. #ifndef SCANDIR_CANCEL
  12. #define SCANDIR_CANCEL
  13. struct scandir_cancel_struct
  14. {
  15.         DIR *dp;
  16.         void *v;
  17.         size_t cnt;
  18. };

  19. static void
  20. cancel_handler (void *arg)
  21. {
  22.         struct scandir_cancel_struct *cp = arg;
  23.         size_t i;
  24.         void **v = cp->v;

  25.         for (i = 0; i < cp->cnt; ++i)
  26.                 free (v[i]);
  27.         free (v);
  28.         (void) __closedir (cp->dp);
  29. }
  30. #endif


  31. int
  32. SCANDIR (dir, namelist, select, cmp)
  33.         const char *dir;
  34.         DIRENT_TYPE ***namelist;
  35.         int (*select) (const DIRENT_TYPE *);
  36.         int (*cmp) (const void *, const void *);
  37. {
  38.         DIR *dp = __opendir (dir);
  39.         DIRENT_TYPE **v = NULL;
  40.         size_t vsize = 0;
  41.         struct scandir_cancel_struct c;
  42.         DIRENT_TYPE *d;
  43.         int save;

  44.         if (dp == NULL)
  45.                 return -1;

  46.         save = errno;
  47.         __set_errno (0);

  48.         c.dp = dp;
  49.         c.v = NULL;
  50.         c.cnt = 0;
  51.         __libc_cleanup_push (cancel_handler, &c);

  52.         while ((d = READDIR (dp)) != NULL)
  53.         {
  54.                 int use_it = select == NULL;

  55.                 if (! use_it)
  56.                 {
  57.                         use_it = select (d);
  58.                         /* The select function might have changed errno.  It was
  59.                            zero before and it need to be again to make the latter
  60.                            tests work.  */
  61.                         __set_errno (0);
  62.                 }

  63.                 if (use_it)
  64.                 {
  65.                         DIRENT_TYPE *vnew;
  66.                         size_t dsize;

  67.                         /* Ignore errors from select or readdir */
  68.                         __set_errno (0);

  69.                         if (__builtin_expect (c.cnt == vsize, 0))
  70.                         {
  71.                                 DIRENT_TYPE **new;
  72.                                 if (vsize == 0)
  73.                                         vsize = 10;
  74.                                 else
  75.                                         vsize *= 2;
  76.                                 new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v));
  77.                                 if (new == NULL)
  78.                                         break;
  79.                                 v = new;
  80.                                 c.v = (void *) v;
  81.                         }

  82.                         dsize = &d->d_name[_D_ALLOC_NAMLEN (d)] - (char *) d;
  83.                         vnew = (DIRENT_TYPE *) malloc (dsize);
  84.                         if (vnew == NULL)
  85.                                 break;

  86.                         v[c.cnt++] = (DIRENT_TYPE *) memcpy (vnew, d, dsize);
  87.                 }
  88.         }

  89.         if (__builtin_expect (errno, 0) != 0)
  90.         {
  91.                 save = errno;

  92.                 while (c.cnt > 0)
  93.                         free (v[--c.cnt]);
  94.                 free (v);
  95.                 c.cnt = -1;
  96.         }
  97.         else
  98.         {
  99.                 /* Sort the list if we have a comparison function to sort with.  */
  100.                 if (cmp != NULL)
  101.                         qsort (v, c.cnt, sizeof (*v), cmp);

  102.                 *namelist = v;
  103.         }

  104.         __libc_cleanup_pop (0);

  105.         (void) __closedir (dp);
  106.         __set_errno (save);

  107.         return c.cnt;
  108. }
复制代码
其中可以看到,在函数退出时,调用了  __libc_cleanup_pop (0); 也就是要调用下面这个函数

static void
cancel_handler (void *arg)
{
        struct scandir_cancel_struct *cp = arg;
        size_t i;
        void **v = cp->v;

        for (i = 0; i < cp->cnt; ++i)
                free (v);
        free (v);
        (void) __closedir (cp->dp);
}
其中free(v)即将v所指向的内存释放掉了,这里的v是cp->v。而在主代码中可以看到 c.v = (void *) v;

看到这里,即scandir函数结束后,会将所有内存释放掉。连 new = (DIRENT_TYPE **) realloc (v, vsize * sizeof (*v)) 这一句所分配的内存也
释放了。
但主函数后面又有  *namelist = v; 使用scandir函数时通过这个namelist来访问排序过后的内存。
但上面的代码里明明就是把分配的内存释放掉了呀。

使用scandir的实例有
  1. #include <dirent.h>

  2.        int
  3.        main(void)
  4.        {
  5.            struct dirent **namelist;
  6.            int n;

  7.            n = scandir(".", &namelist, 0, alphasort);
  8.            if (n < 0)
  9.                perror("scandir");
  10.            else {
  11.                while (n--) {
  12.                    printf("%s\n", namelist[n]->d_name);
  13.                    free(namelist[n]);
  14.                }
  15.                free(namelist);
  16.            }
  17.        }
复制代码
可以看到上面 free(namelist); 又将namelist指向的内存释放了一遍。。
这样一来不就free了两次了吗?

请指点!谢谢各位。

论坛徽章:
0
2 [报告]
发表于 2010-03-23 13:33 |只看该作者
The scandir() function scans the directory dir, calling select() on each directory entry. Entries for which select() returns non-zero are stored in strings allocated via malloc(), sorted using qsort() with the comparison function compar(), and collected in array namelist which is allocated via malloc(). If select is NULL, all entries are selected.
您需要登录后才可以回帖 登录 | 注册

本版积分规则 发表回复

  

北京盛拓优讯信息技术有限公司. 版权所有 京ICP备16024965号-6 北京市公安局海淀分局网监中心备案编号:11010802020122 niuxiaotong@pcpop.com 17352615567
未成年举报专区
中国互联网协会会员  联系我们:huangweiwei@itpub.net
感谢所有关心和支持过ChinaUnix的朋友们 转载本站内容请注明原作者名及出处

清除 Cookies - ChinaUnix - Archiver - WAP - TOP