The dirent struct will be immediately followed in memory by a block of memory that contains the rest of the name, and that memory is accessible through the d_name field.
This is a pattern used in C to indicate an arbitrary-length array at the end of a structure. Arrays in C have no built-in bounds checking, so when your code tries to access the string starting at d_name, it will continue past the end of the structure. This relies on readdir()
will allocate enough memory to hold the entire string plus the terminating nul.
It looks like a micro-optimization to me. Names are commonly short, so why allocate space that you know will go unused. Also, Solaris may support names longer than 255 characters. To use such a struct you just allocate the needed space and ignore the supposed array size.
As others have pointed out, the last member of the struct doesn't have any set size. The array is however long the implementation decides it needs to be to accommodate the characters it wants to put in it. It does this by dynamically allocating the memory for the struct, such as with malloc
.
It's convenient to declare the member as having size 1, though, because it's easy to determine how much memory is occupied by any dirent
variable d
:
sizeof(dirent) + strlen(d.d_name)
Using size 1 also discourages the recipient of such struct values from trying to store their own names in it instead of allocating their own dirent
values. Using the Linux definition, it's reasonable to assume that any dirent
value you have will acept a 255-character string, but Solaris makes no guarantee that its dirent
values will store any more characters than they need to.
I think it was C 99 that introduced a special case for the last member of a struct. The struct could be declared like this instead:
typedef struct dirent {
ino_t d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[];
} dirent_t;
The array has no declared size. This is known as the flexible array member. It accomplishes the same thing as the Solaris version, except that there's no illusion that the struct by itself could hold any name. You know by looking at it that there's more to it.
Using the "flexible" declaration, the amount of memory occupied would be adjusted like so:
sizeof(dirent) + strlen(d.d_name) + 1
That's because the flexible array member does not factor in to the size of the struct.
The reason you don't see flexible declarations like that more often, especially in OS library code, is likely for the sake of compatibility with older compilers that don't support that facility. It's also for compatibility with code written to target the current definition, which would break if the size of the struct changed like that.