So C99 blessed the commonly-used “flexible array member” hack to allow us to make structs 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 mylists 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?
mallocitself doesn’t care at all how much memory you allocate for a structure, it’s the dereferencing of memory outside the block that is undefined. From C996.5.3.2 Address and indirection operators:And, from
7.20.3 Memory management functions, we find (my italics):Hence, you can do something like:
and, provided you only ever try to do anything with
c->ch[0], it’s perfectly acceptable.For your specific concrete example, I’m not too sure I’d be that worried, assuming that what you’re concerned about is storage space. If you’re concerned for other reasons, feel free to ignore this bit, especially since the assumptions included within are not mandated by the standard.
From my understanding, you have a structure:
where you want to use only
datawheresizeis 0, and bothdataandposwheresizeis greater than 0. This precludes the use of puttingdataandposin a union.A significant number of
mallocimplementations will round up your requested space to a multiple of 16 bytes (or some greater power of two) in order to ease memory fragmentation issues. This isn’t required by the standard of course, but it is pretty common.Assuming (for example) 32-bit pointers and
size_t, your twelve bytes of structure will most likely take up a 16-byte arena header and a 16-byte chunk for the data. This chunk would still be 16 bytes even if you only asked for 8 (ie. withoutpos).If you had 64-bit pointer and
size_ttypes, it might make a difference – 24 bytes withposand 16 without.But even then, unless you’re allocating a lot of these structures, it may not be a problem.