8

I want to declare a struct with a flexible array member in it, and then use sizeof() on it. The prototype is:

typedef struct
{
    uint16_t    length;
    uint8_t     array[][2];
} FLEXIBLE_t;

I then declare it:

const FLEXIBLE_t test = {
    .length = sizeof(test),
    .array = {  {   0,  1   },
                {   2,  3   },
                {   4,  5   },
                {   6,  7   },
                {   8,  9   } }
};

Everything compiles okay (GCC) but when I examine test.length it has the value 2, i.e. it is only counting the uint16_t for length itself.

How can I calculate the size of the struct at compile time? It appears that the compiler uses the prototype rather than the specific instance.

  • Ah, hang on, 6.7.2.18 of the standard seems to suggest that this declaration is invalid, even though the compiler accepts it. –  Jan 29 '17 at 16:29
  • Looking at the .map file it seems that GCC has allocated the correct amount of space for the struct and populated it as I would expect. –  Jan 29 '17 at 16:34
  • What is sizeof test if you compute at runtime? – Vagish Jan 29 '17 at 16:39
  • Note that if `test` is defined inside a function without the `static` qualifier (`const` is not material), then GCC does not allow the initialization of a structure with a flexible array member (FAM). If the variable has file scope or is marked `static`, then GCC, as an extension, allows the initialization. – Jonathan Leffler Jan 29 '17 at 16:41
  • @Vagish, sizeof is a compile time constant. –  Jan 29 '17 at 16:43
  • At least you should have the `.length` assignment _after_ the array. But see comments for more formal notes. – Paul Ogilvie Jan 29 '17 at 16:56

4 Answers4

12

sizeof ignores the flexible array member because flexible array member takes no space within a structure.

C11-§6.7.2.2/18

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. [...]

Note that standard C doesn't permit the flexible array member initialization as in your code. It will invoke undefined behavior (see §6.7.2.2 para 20 and 21). While a GCC permit this as an extension:

GCC allows static initialization of flexible array members. This is equivalent to defining a new structure containing the original structure followed by an array of sufficient size to contain the data.

Community
  • 1
  • 1
haccks
  • 104,019
  • 25
  • 176
  • 264
5

A flexible array member doesn't count for the size:

... 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.

In addition to the size problem, your code has undefined behavior in C. A flexible array member can't be initialized like that. Gcc might have an extension in that sense, but that isn't portable.

Jens Gustedt
  • 76,821
  • 6
  • 102
  • 177
2

GCC allows initializing flexible arrays as an extension: https://gcc.gnu.org/onlinedocs/gcc-4.4.0/gcc/Zero-Length.html

However, sizeof() follows the C standard and considers flexible arrays in structs to have a size of zero. In any case, when trying to use sizeof() in the initializer of the struct, the struct is incomplete at that stage and the final size not yet known. Only the size of the prototype, with its zero-length flexible array, is known.

The size is known at compile time however, just not until after the struct has been initialized. GCC's __builtin_object_size() will evaluate to a numeric constant in this instance, but must be called from a function as it is not always constant and as such cannot be used in the initializer.

So .length must be assigned at run time, but at least the value being assigned compiles down to a constant:

test.length = __builtin_object_size(test, 0);
  • Even for a global variable, you still get 0 size for flexible array part when evaluate it in run-time, which is a later stage. – ywu May 09 '21 at 15:46
1

The size returned from the sizeof() operator (almost) ignores the flexible array.

Per the C Standard, 6.7.2.1 Structure and union specifiers, paragraph 18:

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.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
Andrew Henle
  • 32,625
  • 3
  • 24
  • 56