So C99 blessed the commonly-used "flexible array member" hack to allow us to make struct
s that could be overallocated to suit our size requirements. I suspect it's perfectly safe on most sane implementations to do this, but is it legal in C to "underallocate" if we know in certain situations that we won't need some members of a struct
?
Abstract Example
Say I have a type:
struct a {
bool data_is_x;
void * data;
size_t pos;
};
If data_is_x
, then the type of data
is a type that needs to use the pos
member. Otherwise, the functions that work with this struct
won't need the pos
member for this particular copy of the struct
. Essentially, the struct
carries around information about whether or not it has a pos
member, and this information will not be changed within the struct
's lifetime (outside of evil mischief, which will break pretty much anything anyway). Is it safe to say:
struct a *a = malloc(data_is_x ? sizeof(struct a) : offsetof(struct a, pos));
which will allocate space for a pos
member only if one is needed? Or does it violate a constraint to use cast space that is too small to a struct
pointer, even when you never use the members in question?
Concrete Example
My real-world use case is a bit involved; it's here mainly so you can understand why I want to do this:
typedef struct {
size_t size;
void * data;
size_t pos;
} mylist;
The code for mylist_create
specifies that, for size > 0
, data
is an array of contiguous data that is size
items long (whatever an item may be), but that for size == 0
it is the current node of a doubly-linked list containing the items. All the functions that work with mylist
s will check whether size == 0
. If it does, they'll handle the data as a linked list with the "current" index being whichever node data
points to. If not, they'll handle the data as an array with the "current" index stored in pos
.
Now if size == 0
we don't really need the pos
member, but if size > 0
we will. So my question is, is it legal to do this:
mylist *list = malloc(size ? sizeof(mylist) : offsetof(mylist, pos));
If we guarantee (on penalty of undefined behavior) that, while size == 0
, we will never try to (or need to) access the pos
member? Or does it say somewhere in the standard that it's UB to even think about doing this?