Using the sizeof operator in the initialization of a struct with a flexible array
Asked Answered
E

4

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.

Ellette answered 29/1, 2017 at 16:23 Comment(6)
Ah, hang on, 6.7.2.18 of the standard seems to suggest that this declaration is invalid, even though the compiler accepts it.Ellette
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.Ellette
What is sizeof test if you compute at runtime?Splenetic
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.Dekameter
@Vagish, sizeof is a compile time constant.Ellette
At least you should have the .length assignment after the array. But see comments for more formal notes.Proto
E
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);
Ellette answered 29/1, 2017 at 16:42 Comment(1)
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.Susquehanna
A
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.

Arrogant answered 29/1, 2017 at 16:27 Comment(2)
Indeed, see my comment on the question, I think the declaration may be invalid even though GCC is happy with it.Ellette
@ゼーロ; GCC allows it as an extension.Arrogant
F
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.

Frizzle answered 29/1, 2017 at 16:31 Comment(0)
E
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);
Ellette answered 29/1, 2017 at 16:42 Comment(1)
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.Susquehanna
A
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.

Astrolabe answered 29/1, 2017 at 16:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.