3

The C standard states (emphasize mine):

21 EXAMPLE 2 After the declaration:

struct s { int n; double d[]; };

the structure struct s has a flexible array member d. [...]


22 Following the above declaration:

struct s t1 = { 0 };         // valid
struct s t2 = { 1, { 4.2 }}; // invalid
t1.n = 4;                    // valid
t1.d[0] = 4.2;               // might be undefined behavior

The initialization of t2 is invalid (and violates a constraint) because struct s is treated as if it did not contain member d.

Source: C18, §6.7.2.1/20 + /21

I do not understand the explanation of "because struct s is treated as if it did not contain member d"

If I use the initializer of { 1, { 4.2 }};, the { 4.2 } part is to initialize the flexible array member; To be precise to initialize the flexible array member to be consisted of one element and initialize this element to the value 4.2 and thus stuct s is treated as it has member d or not?

This sentence makes no sense in my eyes.

  • Why does the standard say, that { 4.2 } wouldn't initialize/denote the flexible array member and thus the structure would be treated as if it has no member d?

If I use a fixed size array, this notation works and initializes the member with no complain:

struct foo {
    int x;
    double y[1];
};

int main (void)
{
    struct foo a = { 1, { 2.3 } };
}

Evidence

  • Why is this initialization invalid when the structure has an flexible array member but valid when the structure has an fixed size array member?

Could you elaborate that?


I've read:

Why does static initialization of flexible array member work?

and

How to initialize a structure with flexible array member

and

Flexible array members can lead to undefined behavior?

and others but none of them answers me what this sentence wants to explain and why exactly this this is invalid.


Related:

4 Answers4

2

I do not understand the explanation of "because struct s is treated as if it did not contain member d".

The C standard also says “In most situations, the flexible array member is ignored.” It is unclear why you would not understand what the meaning of this is. If struct s is declared struct s { int n; double d[]; };, then, in most situations, the C implementation behaves as if it were declared struct s { int n; };. Therefore, struct s t2 = { 1, { 4.2 }}; fails because the 4.2 is an initializer for something that, in effect, does not exist.

It is sensible to ask why this is the situation. For the most part, I expect a compiler could support a definition in which the array initializers were counted and used to set the structure size. Certainly compilers do this with array definitions such s int a[] = { 3, 4, 5};. However, that is not the typical use case for flexible array members. Typically, a program receives information about how many elements it will need to manage with the structure, allocates space for the structure with space for those elements included, and then puts a structure in the allocated space. That is, the typical use case for structures with flexible array members is with dynamically allocated space. I expect the C committee saw little need to require compilers to support flexible array members in static or automatic objects, instead of dynamic objects.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
  • 1
    "*That phrase does not appear in the C standard*" - I can find it in n1124 (C99+T1/T2), n1570 (C11) and n2310 (C18) - Even if with different paragraph numbers for the formers. Although they are indeed drafts, I can't think of that this wouldn't be part of the standard. – RobertS supports Monica Cellio Jul 07 '20 at 12:06
  • 1
    The example exists in the actual ISO standards too. – Lundin Jul 07 '20 at 12:12
  • @RobertSsupportsMonicaCellio: I searched the official version of C 2018 and draft n1570 of C 2011 for “contains” (to avoid the possibility of line breaks or other formatting interfering with a search for the whole phrase) and did not find such a phrase in either draft. If you believe it is present in either one, then cite the specific clause and paragraph numbers. If you are using a source on the web, give its URL. – Eric Postpischil Jul 07 '20 at 12:13
  • 1
    @EricPostpischil I did so in the question: C18, §6.7.2.1/20 + /21 - The text in question is at /21. – RobertS supports Monica Cellio Jul 07 '20 at 12:14
  • @RobertSsupportsMonicaCellio: Okay, sorry. I am looking at it right now, and my PDF viewer is not showing it as a hit! I will file a bug report. – Eric Postpischil Jul 07 '20 at 12:16
  • @EricPostpischil Is the bug report available online? – Janus Troelsen May 18 '21 at 05:54
  • @JanusTroelsen: Not publicly; Apple bug reports are generally private. – Eric Postpischil May 18 '21 at 07:35
2

I guess this is a language defect. While it might make no sense to initialize a flexible array member, the standard needs to address that issue somewhere. I can't find such normative text anywhere.

The definition of a flexible array member is, C17 6.7.2.1/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.

From this we learn that a flexible array member is an incomplete array type. We do not however learn in what situations the flexible array member is ignored, save for when calculating the size of the struct. "In most situations" isn't helpful and is the defect - this needed to be expanded to an exhaustive list, including the behavior of flexible array members when part of an initializer list. Otherwise one may assume that it behaves just like any other array of incomplete type.

C17 6.2.5/22:

An array type of unknown size is an incomplete type.

And then the rules for initialization say, C17 6.7.9:

The type of the entity to be initialized shall be an array of unknown size or a complete object type that is not a variable length array type.

So far there is no normative text saying that we are not allowed to provide an initializer for a flexible array member - on the contrary. The example in the question (C17 6.7.2.1 example 21) is not normative, since examples aren't normative in ISO standards. The example doesn't mention which constraint that is violated, nor does it mention where it says that the flexible array member must be ignored.

I suppose I'd probably file a DR about this.

Lundin
  • 195,001
  • 40
  • 254
  • 396
  • I didn't knew that we can still post DRs. I thought the committee suspended the feedback from non-internal people. Recently I wanted to look for doing a DR, but I couldn't found a opportunity. – RobertS supports Monica Cellio Jul 07 '20 at 12:27
  • 1
    @RobertSsupportsMonicaCellio You'll probably have to go through your national standard organization and contact the ISO WG through them. I searched for DR but couldn't find any. This is supposedly the original one that lead to the introduction of flexible array members: http://www.open-std.org/jtc1/sc22/wg14/www/docs/n791.htm and the text has changed a lot since that early proposal. – Lundin Jul 07 '20 at 12:34
  • I'm also in doubt with the classification as "*invalid*" (although non-normative) because an compiler can indeed allow to do so, as shown with GCC. "*invalid*" suggests IMHO that the example is completely prohibited/invalid. It should be stated as "the behavior is unspecified/undefined" instead as it is similar at the fourth example. – RobertS supports Monica Cellio Jul 07 '20 at 13:02
1

You've omitted some important language in the example you quoted - here's the full text:

20 EXAMPLE 2 After the declaration:

struct s { int n; double d[]; };
the structure struct s has a flexible array member d. A typical way to use this is:

int m = /* some value */;
struct s *p = malloc(sizeof (struct s) + sizeof (double [m]));
and assuming that the call to malloc succeeds, the object pointed to by p behaves, for most purposes, as if p had been declared as:

struct { int n; double d[m]; } *p;
(there are circumstances in which this equivalence is broken; in particular, the offsets of member d might not be the same).

IOW, flexible array members only really come into play if you allocate the struct instance dynamically and allocate additional space for the array member.

A flexible array member has no size, so it doesn't contribute to the size of the struct type - that is, the result of sizeof (struct s) evaluates to the size of the type without the array.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • "*A typical way*" means not "*The only way*". They didn't prohibited the use as `static` object of a struct with a FAM explicitly (Which you didn't said, I know, but may your answer in whole suggests). They only declared this specific (static) initialization as invalid, although GCC allows this as extension still. I wanted to find out why they did declared it so. Also as @Lundin already hinted, the examples aren't normative, so we shouldn't focus too long on those only. – RobertS supports Monica Cellio Jul 07 '20 at 14:29
  • Also the last sentence is not quite correct. It is correct that no size for the FAM itself is added to the size of the structure, but that it doesn't contribute to the size of the `struct` type is not correct, it can have an influence on padding: "*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.*", C18 6.7.2.1/18. – RobertS supports Monica Cellio Jul 07 '20 at 14:29
0

IMO it is because sizeof of the initialized this way struct cannot be determined in another compilation unit when the struct is declared as extern.

0___________
  • 60,014
  • 4
  • 34
  • 74