Ditch the “Struct Hack”: Master C’s Flexible Array Members
Ever needed to work with data that has a fixed-size header but a variable-length payload in C? Maybe you’re reading from a network socket, parsing a file format, or just need to bundle some metadata with a dynamic buffer. For years, C programmers relied on a clever but non-standard trick known as the “struct hack.”
But since 1999, there’s been a better, safer, and standard-compliant way. Let’s dive into the flexible array member (FAM).
The Old Way: The “Struct Hack” 🤔
First, a quick look at the past. The “struct hack” typically looked like this:
// The OLD, non-standard way
struct OldPacket {
int packet_id;
int data_len;
char data[1]; // Or sometimes data[0]
};Programmers would allocate sizeof(struct OldPacket) + extra_data_size bytes and then access the data member as if it were a much larger array. While it often worked, it relied on compiler-specific behavior and technically invoked undefined behavior by reading past the declared boundary of the array. It's a clever hack, but it's not portable or guaranteed to be safe.
The C99 Solution: The Flexible Array Member ✅
The C99 standard introduced the flexible array member to formalize this pattern. It’s declared as the very last member of a struct, using empty brackets.
struct HeaderedData {
size_t capacity; // Or any other header data you need
int item_count;
char data[]; // The flexible array member!
};Here are the simple rules:
The FAM must be the last member of the
struct.The
structmust have at least one other named member.
When you use sizeof on this struct, it returns the size of everything except the flexible member. The compiler knows that its size will be determined at runtime.
// This only gives you the size of 'capacity' and 'item_count'.
printf("Size of header: %zu bytes\n", sizeof(struct HeaderedData));Allocation: Bringing it to Life 🚀
You can’t create an instance of a struct with a FAM on the stack; it must be dynamically allocated on the heap. The key is to calculate the total memory needed for both the header and your variable data in a single block.
The allocation formula is simple:
total_size = sizeof(your_struct)+(number_of_elements×sizeof(element_type))Let’s see a complete, practical example.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// Our modern struct with a FAM
struct HeaderedData {
size_t capacity;
char data[];
};
int main() {
// 1. Let's decide what data we want to store.
const char *my_string = "This is dynamically allocated!";
size_t data_size = strlen(my_string) + 1; // +1 for the null terminator
// 2. Calculate the total size needed.
size_t total_size = sizeof(struct HeaderedData) + data_size;
printf("Header size: %zu\n", sizeof(struct HeaderedData));
printf("Data size: %zu\n", data_size);
printf("Total allocation size: %zu\n", total_size);
// 3. Allocate one contiguous block of memory.
struct HeaderedData *my_data = malloc(total_size);
// 4. ALWAYS check for allocation failure.
if (my_data == NULL) {
perror("Failed to allocate memory");
return 1;
}
// 5. Initialize the struct.
// We can now use the members as if they were declared normally.
my_data->capacity = data_size;
strcpy(my_data->data, my_string); // Copy our string into the flexible part.
// Let's prove it works!
printf("\nHeader capacity: %zu\n", my_data->capacity);
printf("Data content: '%s'\n", my_data->data);
// Clean up is simple!
free(my_data);
my_data = NULL; // Good practice to prevent use-after-free.
return 0;
}The Payoff: Simplicity and Safety
Why is this so great?
Clarity: The
char data[]syntax clearly and explicitly states your intent. Any C programmer familiar with modern standards will know exactly what you're doing.Safety: You are no longer relying on undefined behavior. This is the official, portable, and compiler-approved method.
Efficiency: You perform a single
mallocand a singlefree. This is often faster than two separate allocations and can help reduce memory fragmentation. The entire data structure lives in one contiguous block of memory, which can also be beneficial for caching.
So next time you need to bundle data with a header, leave the “struct hack” in the past. Embrace the C99 flexible array member for cleaner, safer, and more professional C code.


