简体   繁体   中英

Allocating a struct dirent without malloc()

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 ?

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. */
};

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);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM