7

This is C99 code:

typedef struct expr_t
{
    int n_children; 
    foo data; // Maybe whatever type with unknown alignment
    struct expr_t *children[];
} expr_t;

Now, how do I allocate memory ?

expr_t *e = malloc (sizeof (expr_t) + n * sizeof (expr_t *));

or

expr_t *e = malloc (offsetof (expr_t, children) + n * sizeof (expr_t *));

?

Is sizeof even guaranteed to work on an type with flexible array member (GCC accepts it) ?

Alexandre C.
  • 55,948
  • 11
  • 128
  • 197
  • 3
    Yes, it's guaranteed to work. C99 §6.7.2.1/16 says "the size of the structure shall be equal to the offset of the last element of an otherwise identical structure that replaces the flexible array member with an array of unspecified length.". So `sizeof(expr_t)` equals the value that `offsetof(expr_t, children)` if you had redefined `children` to be an array of any determinate size. – Adam Rosenfield Oct 01 '12 at 21:03

2 Answers2

11

expr_t *e = malloc (sizeof (expr_t) + n * sizeof (expr_t *)); is well defined in C99. From the C99 specification 6.7.2.1.16:

As a special case, the last element of a structure with more than one named member may have an incomplete array type; this is called a flexible array member. In most situations, the flexible array member is ignored. In particular, the size of the structure is as if the flexible array member were omitted except that it may have more trailing padding than the omission would imply.

Blagovest Buyukliev
  • 42,498
  • 14
  • 94
  • 130
CrazyCasta
  • 26,917
  • 4
  • 45
  • 72
4

If the compiler accepts the declaration of a struct with a flexible array member, the sizeof operator for that struct should yield the size of the struct as if the flexible array member doesn't exist.

The correct allocation of such a struct would be:

expr_t *e = malloc (sizeof(expr_t) + n * sizeof(struct expr_t *));

You can still do this trick even if flexible array members are not supported by the compiler. Just declare the array member of your struct as having a size of 1, and then allocate n - 1 items instead of n.

Blagovest Buyukliev
  • 42,498
  • 14
  • 94
  • 130
  • 3
    Slight caveat: the padding/alignment requirement for a structure will be dictated by the most restrictive element; a flexible array element whose type requires more restrictive alignment than anything else in the struct may thus change the padding and alignment of the struct. For example, on some machines a struct which contains a `char` and a `float[]` might be 4 bytes and require four-byte alignment, even though without the `double[]` it would only be one byte. – supercat Dec 05 '12 at 16:48