Flexible Array Members in C99
The management of variable-sized arrays in C structures has changed thanks to the introduction of flexible array members in the C99 standard.
This mechanism replaces the traditional struct hack, offering an official and cleaner solution for dynamically allocating memory portions within a structure.
In what follows, we will see how to declare and use flexible array members and what constraints apply to this type of structure.
Struct Hack Technique
Occasionally, it may be necessary to define a structure that contains an array of unknown size.
For example, we might want to store strings in a format different from the usual one. Normally, a string is an array of characters with a null character at the end. However, there are advantages to storing strings in other ways. An alternative is to store the string length along with the characters (but without the null character).
Length and characters could be stored in a structure like this:
struct vstring {
int len;
char chars[N];
};
Here N is a macro representing the maximum length of a string.
The use of a fixed-length array like this is however inadvisable, as it forces us to limit the string length and wastes memory (since most strings will not occupy all N characters of the array).
C programmers have traditionally solved this problem by declaring the length of chars equal to 1 (a dummy value) and dynamically allocating each string:
struct vstring {
int len;
char chars[1];
};
...
struct vstring *str = malloc(sizeof(struct vstring) + n - 1);
str->len = n;
In this case, we are cheating by allocating more memory than the structure declares to have (in this example, n - 1 additional characters) and using that memory to store the additional elements of the chars array. This technique has become so common over the years that it deserves a name: the struct hack.
Struct Hack
The Struct Hack is a technique for defining structures in C that contain arrays of unknown size as the final member of the structure itself.
To use the struct hack, you declare an array of size 1 as the last member of the structure. When allocating memory for the structure, you add the actual size of the required array minus 1 to the memory allocation.
The syntax of the struct hack is as follows:
struct structure_name {
/* structure members */
array_type array_name[1];
};
The memory allocation for a structure using the struct hack is as follows:
struct structure_name *p = malloc(
sizeof(struct structure_name) +
sizeof(array_type) * (size - 1)
);
The struct hack is not limited to character arrays: it has a wide variety of uses. Over time it has become so widespread that many compilers support it. Some (including GCC) even allow the chars array to have zero length, making this trick even more explicit. Unfortunately, the C89 standard does not guarantee the functioning of the struct hack, nor does it allow zero-length arrays.
For example in GCC, the following code is legal:
struct vstring {
int len;
char chars[0];
};
In this case, chars is a zero-length array. When allocating memory for a vstring structure, you can write:
struct vstring *str = malloc(sizeof(struct vstring) + n);
str->len = n;
In this example, str points to a vstring structure in which the chars array occupies n characters. The sizeof operator ignores the chars array when calculating the structure size.
Flexible Array Members in C99
Recognizing the usefulness of the struct hack, the C99 standard provides a feature known as flexible array member, which serves the same purpose. When the last member of a structure is an array, its length can be omitted:
struct vstring {
int len;
char chars[]; /* flexible array member – only in C99 */
};
The length of the chars array is not determined until memory for a vstring structure is allocated, usually with a call to malloc:
struct vstring *str = malloc(sizeof(struct vstring) + n);
str->len = n;
In this example, str points to a vstring structure in which the chars array occupies n characters. The sizeof operator ignores the chars member when calculating the structure size. (A flexible array member is unusual because it does not occupy space within the structure itself.)
Some special rules apply to a structure containing a flexible array member:
- The flexible array member must appear as the last member of the structure;
- The structure must have at least one other member;
- If we copy a structure containing a flexible array member, only the other members will be copied but not the flexible array itself.
In summary:
Flexible Array Member in C99
A flexible array member is an array of unknown length that appears as the last member of a structure in C99.
The length of the flexible array is not specified in the structure declaration, but is determined when memory for the structure is allocated.
To use a flexible array member, you declare an array without specifying its length as the last member of the structure:
struct structure_name {
/* structure members */
array_type array_name[];
};
The memory allocation for a structure with a flexible array member is as follows:
struct structure_name *p = malloc(
sizeof(struct structure_name) +
sizeof(array_type) * size
);
A structure with a flexible array member must have at least one other member. When copying a structure with a flexible array member, only the other members will be copied, not the flexible array itself.
Introduction to Incomplete Types
A structure containing a flexible array member is an incomplete type.
An incomplete type does not provide all the information necessary to determine how much memory it needs. Incomplete types, which we will cover thoroughly in the next lessons, are subject to several restrictions.
In particular, an incomplete type (and therefore a structure containing a flexible array member) cannot be a member of another structure nor an element of an array. However, any array can contain pointers to structures that have a flexible array member.
In Summary
In this lesson we have seen that:
- Flexible array members are a feature introduced by the C99 standard to handle structures with arrays of unknown size at compile time.
- They replace the so-called struct hack, making the use of dynamic arrays within structures safer and more portable.
- The flexible member must be the last in the structure and the structure itself must contain at least one other member.
- The type of the structure containing the flexible member is considered incomplete and subject to various limitations (for example, it cannot be included as a member in another structure).
- This approach allows allocating the necessary memory for the flexible part at the time of use, avoiding waste and fixed size constraints.