Another usage of zero length array is as a named label inside a struct to assist compile time struct offset check.
Suppose you have some large struct definitions (spans multiple cache lines) that you want to make sure they are aligned to cache line boundary both in the beginning and in the middle where it crosses the boundary.
struct example_large_s
{
u32 first; // align to CL
u32 data;
....
u64 *second; // align to second CL after the first one
....
};
In code you can declare them using GCC extensions like:
__attribute__((aligned(CACHE_LINE_BYTES)))
But you still want to make sure this is enforced in runtime.
ASSERT (offsetof (example_large_s, first) == 0);
ASSERT (offsetof (example_large_s, second) == CACHE_LINE_BYTES);
This would work for a single struct, but it would be hard to cover many structs, each has different member name to be aligned. You would most likely get code like below where you have to find names of the first member of each struct:
assert (offsetof (one_struct, <name_of_first_member>) == 0);
assert (offsetof (one_struct, <name_of_second_member>) == CACHE_LINE_BYTES);
assert (offsetof (another_struct, <name_of_first_member>) == 0);
assert (offsetof (another_struct, <name_of_second_member>) == CACHE_LINE_BYTES);
Instead of going this way, you can declare a zero length array in the struct acting as a named label with a consistent name but does not consume any space.
#define CACHE_LINE_ALIGN_MARK(mark) u8 mark[0] __attribute__((aligned(CACHE_LINE_BYTES)))
struct example_large_s
{
CACHE_LINE_ALIGN_MARK (cacheline0);
u32 first; // align to CL
u32 data;
....
CACHE_LINE_ALIGN_MARK (cacheline1);
u64 *second; // align to second CL after the first one
....
};
Then the runtime assertion code would be much easier to maintain:
assert (offsetof (one_struct, cacheline0) == 0);
assert (offsetof (one_struct, cacheline1) == CACHE_LINE_BYTES);
assert (offsetof (another_struct, cacheline0) == 0);
assert (offsetof (another_struct, cacheline1) == CACHE_LINE_BYTES);