Is using flexible array members in C bad practice? Is using flexible array members in C bad practice? arrays arrays

Is using flexible array members in C bad practice?


It is an accepted "fact" that using goto is poor software engineering practice. That doesn't make it true. There are times when goto is useful, particularly when handling cleanup and when porting from assembler.

Flexible array members strike me as having one main use, off the top of my head, which is mapping legacy data formats like window template formats on RiscOS. They would have been supremely useful for this about 15 years ago, and I'm sure there are still people out there dealing with such things who would find them useful.

If using flexible array members is bad practice, then I suggest that we all go tell the authors of the C99 spec this. I suspect they might have a different answer.


PLEASE READ CAREFULLY THE COMMENTS BELOW THIS ANSWER

As C Standardization move forward there is no reason to use [1] anymore.

The reason I would give for not doing it is that it's not worth it to tie your code to C99 just to use this feature.

The point is that you can always use the following idiom:

struct header {  size_t len;  unsigned char data[1];};

That is fully portable. Then you can take the 1 into account when allocating the memory for n elements in the array data :

ptr = malloc(sizeof(struct header) + (n-1));

If you already have C99 as requirement to build your code for any other reason or you are target a specific compiler, I see no harm.


No, using flexible array members in C is not bad practice.

This language feature was first standardized in ISO C99, 6.7.2.1 (16). In the following revision, ISO C11, it is specified in Section 6.7.2.1 (18).

You can use them like this:

struct Header {    size_t d;    long v[];};typedef struct Header Header;size_t n = 123; // can dynamically change during program execution// ...Header *h = malloc(sizeof(Header) + sizeof(long[n]));h->n = n;

Alternatively, you can allocate like this:

Header *h = malloc(sizeof *h + n * sizeof h->v[0]);

Note that sizeof(Header) includes eventual padding bytes, thus, the following allocation is incorrect and may yield a buffer overflow:

Header *h = malloc(sizeof(size_t) + sizeof(long[n])); // invalid!

A struct with a flexible array members reduces the number of allocations for it by 1/2, i.e. instead of 2 allocations for one struct object you need just 1. Meaning less effort and less memory occupied by memory allocator bookkeeping overhead. Furthermore, you save the storage for one additional pointer. Thus, if you have to allocate a large number of such struct instances you measurably improve the runtime and memory usage of your program (by a constant factor).

In contrast to that, using non-standardized constructs for flexible array members that yield undefined behavior (e.g. as in long v[0]; or long v[1];) obviously is bad practice. Thus, as any undefined-behaviour this should be avoided.

Since ISO C99 was released in 1999, more than 20 years ago, striving for ISO C89 compatibility is a weak argument.