问题
I need to use readdir_r()
to read the contents of a directory in a multithreaded program. Since the size of struct dirent
is filesystem dependent, man readdir_r
recommends
name_max = pathconf(dirpath, _PC_NAME_MAX);
if (name_max == -1) /* Limit not defined, or error */
name_max = 255; /* Take a guess */
len = offsetof(struct dirent, d_name) + name_max + 1;
to find the size of the allocation needed. To allocate it
entryp = malloc(len);
is called, and finally readdir_r()
uses it like this:
struct dirent *returned;
readdir_r(DIR*, entryp, &returned);
However, I'd like to avoid calling malloc()
(or any other manual memory management function).
One way I've thought of is
_Alignas(struct dirent) char direntbuf[len];
struct dirent *entryp = (struct dirent*) direntbuf;
This should give a correctly aligned allocation, but it violates strict aliasing. However, the buffer is never accessed via a char*
so the most likely problem, the compiler reordering accesses to the buffer via different types, cannot occur.
Another way could be by alloca()
, which returns a void*
, avoiding strict aliasing problems. However, alloca()
does not seem to guarantee alignment the way malloc()
and friends do. To always get an aligned buffer, something like
void *alloc = alloca(len + _Alignof(struct dirent));
struct dirent *direntbuf = (struct dirent*)((uintptr_t)&((char*)alloc)[_Alignof(struct dirent)]&-_Alignof(struct dirent));
would be needed. In particular, the cast to char *
is needed to perform arithmetic on a pointer, and the cast to uintptr_t
is needed to do the binary &
. This doesn't look more well-defined than allocating a char[]
.
Is there a way to avoid manual memory management when allocating a struct dirent
?
回答1:
What about defining this:
#include <stddef.h> /* For offsetof */
#include <dirent.h>
union U
{
struct dirent de;
char c[offsetof(struct dirent, d_name) + NAME_MAX + 1]; /* NAME_MAX is POSIX. */
};
回答2:
The readdir_r function signature is:
int readdir_r(DIR *dirp, struct dirent *entry, struct dirent **result);
And direct is a struct like this:
struct dirent {
ino_t d_ino; /* inode number */
off_t d_off; /* offset to the next dirent */
unsigned short d_reclen; /* length of this record */
unsigned char d_type; /* type of file; not supported
by all file system types */
char d_name[256]; /* filename */
};
You have to pass a pointer to readdir_r but how you allocate memory for the dirent structure is entirely up to you.
You could do it like this and use a stack variable.
struct dirent entry = {0};
...
readdir_r(DIR*, &entry, &returned);
来源:https://stackoverflow.com/questions/30138626/allocating-a-struct-dirent-without-malloc