Flexible Array Members Done Right
Flexible Array Members Done Right
Thesis
A flexible array member (C99) is the standard way to attach a variable-length payload to a struct without a second allocation. sizeof the struct excludes the payload; you allocate sizeof(header) + n bytes and treat data[] as the tail.
Mental model
malloc(sizeof(struct packet) + n)
┌──────────┬─────────────────────┐
│ len │ data[0 .. n-1] │
└──────────┴─────────────────────┘
↑ sizeof(struct packet) stops here
Rules of thumb:
- The FAM must be the last member.
- Do not declare arrays of the struct type.
- Prefer
sizeof *p + nover guessing padding.
Worked example
Verified with GCC 14.2, -std=c11 -Wall.
/* fam.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stdint.h>
struct packet {
uint16_t len;
char data[]; /* flexible array member */
};
int main(void) {
size_t n = 5;
struct packet *p = malloc(sizeof *p + n);
if (!p) return 1;
p->len = (uint16_t)n;
memcpy(p->data, "hello", n);
printf("sizeof(struct packet)=%zu payload=%.*s len=%u\n",
sizeof(struct packet), (int)p->len, p->data, p->len);
free(p);
return 0;
}gcc -O2 -std=c11 -Wall fam.c -o fam && ./famsizeof(struct packet)=2 payload=hello len=5
sizeof(struct packet) is just the fixed header (here 2 bytes, possibly padded). The five payload bytes live past that.
Trap
struct packet p; p.data[0] = 'x'; on a stack instance. There is no storage for data. Same for struct packet arr[10];. Also avoid the pre-C99 GCC [0] hack in new code — use [].
Boring rule
One allocation: malloc(sizeof *hdr + payload_bytes). Check overflow of the size addition before calling malloc. Free the single pointer.
Try this
- Add a
uint32_t checksumbeforedata[]and re-checksizeof. - Pass
-Wflexible-array-extensions/ pedantic flags and clean warnings. - Compare with a pointer-to-heap design (
char *data) and count syscalls understrace(when available).
Sources
- ISO/IEC 9899:1999 §6.7.2.1 (flexible array members)
- CERT C: MEM33-C