我在Linux环境中使用C编程语言来读取目录中的文件。我在代码中添加了#include<dirent.h>
,并使用了函数readdir()
。
根据在线的Linux页面,它说不会在free()
结构的结果指针上调用dirent
,因为它可能被分配在堆栈上。
你能帮我理解它是如何工作的吗?我不明白为什么我们不必删除struct dirent
。什么时候删除它谁删除它?
Here是我所说的摘录:
成功时,
readdir()
返回指向dirent
结构的指针。 (此结构可以静态分配;不要尝试free(3)
它。)如果到达目录流的末尾,则返回NULL
并且不更改errno
。如果发生错误,则返回NULL
并正确设置errno
。
答案 0 :(得分:10)
man readdir
字面上说:
成功时,
readdir()
返回指向dirent结构的指针。 (这种结构可以静态分配;不要尝试free(3)
它。)
(已添加代码格式化程序。)
这意味着它的空间不是在运行时分配的,例如堆栈或空闲存储内存,而是static
:它在可执行文件本身,与字符串文字相比,区别在于写入字符串文字是未定义的行为。
想象一下这样的实现:
struct dirent *readdir(DIR *dirp) {
static struct dirent dir;
/* Fill dir with appropriate values. */
return &dir;
}
dir
在这里静态分配。返回它的地址是错误的,因为它存在于程序的整个运行时间内。
以下是我的glibc 2.22实现中readdir
的实际源代码(路径为/sysdeps/posix/readdir.c
):
DIRENT_TYPE *
__READDIR (DIR *dirp)
{
DIRENT_TYPE *dp;
int saved_errno = errno;
#if IS_IN (libc)
__libc_lock_lock (dirp->lock);
#endif
do
{
size_t reclen;
if (dirp->offset >= dirp->size)
{
/* We've emptied out our buffer. Refill it. */
size_t maxread;
ssize_t bytes;
#ifndef _DIRENT_HAVE_D_RECLEN
/* Fixed-size struct; must read one at a time (see below). */
maxread = sizeof *dp;
#else
maxread = dirp->allocation;
#endif
bytes = __GETDENTS (dirp->fd, dirp->data, maxread);
if (bytes <= 0)
{
/* On some systems getdents fails with ENOENT when the
open directory has been rmdir'd already. POSIX.1
requires that we treat this condition like normal EOF. */
if (bytes < 0 && errno == ENOENT)
bytes = 0;
/* Don't modifiy errno when reaching EOF. */
if (bytes == 0)
__set_errno (saved_errno);
dp = NULL;
break;
}
dirp->size = (size_t) bytes;
/* Reset the offset into the buffer. */
dirp->offset = 0;
}
dp = (DIRENT_TYPE *) &dirp->data[dirp->offset];
#ifdef _DIRENT_HAVE_D_RECLEN
reclen = dp->d_reclen;
#else
/* The only version of `struct dirent*' that lacks `d_reclen'
is fixed-size. */
assert (sizeof dp->d_name > 1);
reclen = sizeof *dp;
/* The name is not terminated if it is the largest possible size.
Clobber the following byte to ensure proper null termination. We
read jst one entry at a time above so we know that byte will not
be used later. */
dp->d_name[sizeof dp->d_name] = '\0';
#endif
dirp->offset += reclen;
#ifdef _DIRENT_HAVE_D_OFF
dirp->filepos = dp->d_off;
#else
dirp->filepos += reclen;
#endif
/* Skip deleted files. */
} while (dp->d_ino == 0);
#if IS_IN (libc)
__libc_lock_unlock (dirp->lock);
#endif
return dp;
}
我不太了解glibc,但行
dp = (DIRENT_TYPE *) &dirp->data[dirp->offset];
对我们来说似乎最有趣。据我所知,此处dirp->data
是static
数据。
这就是为什么有可重入替代readdir_r
而readdir
不可重入的原因。
想象两个线程同时执行readdir
。两者都会尝试同时填充所有dir
个调用之间共享的readdir
,从而导致无序的内存读/写。
答案 1 :(得分:3)
您引用的 man page 警告struct dirent
statically allocated 。因此free()
不是必需的。
free() 专门用于 [m][c][re]alloc() 函数,这些函数都来自 {{3}的内存请求} 的。 (而不是堆栈)
答案 2 :(得分:1)
您只需要false
先前通过拨打free()
和家人分配的内存malloc()
。这是dynamically allocated memory,来自 heap 区域。
OTOH,非动态分配(请参阅automatic allocation)无需单独处理。一旦变量超出范围,就会回收堆栈内存并根据需要重新使用。
答案 3 :(得分:1)
您不需要释放/释放由readdir()
提取的条目。它使用静态内部缓冲区,因此不需要动态释放,因为它没有动态分配。请注意,编译器可以预测所需的空间,因为您一次只使用一个条目,而您只需要一个条目来存储结果。这就是为什么它也不可重入的原因。有一个readdir_r()
,它接受用户分配的缓冲区,当然是可重入的。
您需要在closedir()
指针上调用DIR *
,以释放opendir()
使用的资源。
答案 4 :(得分:0)
struct dirent
逻辑上是DIR
的一部分。它可能会在同一个readdir()
的后续DIR
来电中重复使用(但不会在readdir()
上的DIR
来电中重复使用),并且会在closedir()
时被释放}。
某些文档指出readdir()
不是线程安全的。在除了非常奇特的实现之外的所有实现中,只要在下一次struct dirent
调用之前对上一个readdir()
的最后一次访问发生,它就是线程安全的。使用readdir_r()
是不可取的,因为很难正确确定NAME_MAX限制,readdir_r()
函数不知道其调用者使用的值。
有关readdir_r()
问题的详细信息,请访问http://austingroupbugs.net/view.php?id=696。