Why is initializing a FAM invalid?

  • Thread starter Johannes Schaub (litb)
  • Start date
J

James Kuyper

On 11/10/2010 02:09 AM, Michael Foukarakis wrote:
....
My quibble is why

struct s { int x; char d[]; } e = { 0, "foo" };

produces the same error, as there is at least one way to resolve it
without conflicting with the wording of the standard.

What is that "one way to resolve it"?

The standard can be read as requiring that an array of char be allocated
for "foo" itself, and that e.d be initialized by copying from that
array. However, there's no way for strictly conforming code to get the
address of that array, and therefore no way for such code to make any
use of that fact.

Aside from that issue, that declaration is semantically equivalent to

struct s { int x; char d[]; } e = { 0, {'f', 'o', 'o', '\0'} };

which still provides initializers for four char objects (e.d[0] through
e.d[3]) for which no space need have been allocated. That's still a
constraint violation (6.7.8p1).
 
M

Michael Foukarakis

On 11/10/2010 02:09 AM, Michael Foukarakis wrote:
...
My quibble is why
         struct s { int x; char d[]; } e = { 0, "foo" };
produces the same error, as there is at least one way to resolve it
without conflicting with the wording of the standard.

What is that "one way to resolve it"?

Assuming "foo" is a string constant and initializing the FAM
accordingly. However, as you also said, gcc is just being consistent,
which is, after some thought, much better than distinguishing between
the two cases.

2c
 
J

James Kuyper

On 11/10/2010 02:09 AM, Michael Foukarakis wrote:
...
My quibble is why
� � � � �struct s { int x; char d[]; } e = { 0, "foo" };
produces the same error, as there is at least one way to resolve it
without conflicting with the wording of the standard.

What is that "one way to resolve it"?

Assuming "foo" is a string constant and initializing the FAM
accordingly. However, as you also said, gcc is just being consistent,
which is, after some thought, much better than distinguishing between
the two cases.

I don't see that as a solution to the basic problem, which is that there
is no guarantee that e is big enough to contain e.d[3], which would
otherwise be initialized by the '\0' terminating that string. It's not
even guaranteed to be big enough to contain e.d[0], which would be
initialized with the 'f'. If offsetof(struct s,d) == sizeof(struct s),
as is perfectly permissible, then the definition of 'e' provides more 4
more initializers than there are objects available to be initialized. So
it still violates the constraint in 6.7.8p1.
 
J

Johannes Schaub (litb)

Wojtek said:
Wojtek said:
Wojtek Lerch wrote:
[...] GCC, for example, allows FAM
initialization as an extension. [...]

What does sizeof return when applied to the name of such object?

To answer my own question, sizeof does not include the FAM. The
analogy with arrays of unknown size is bogus.

The analogy was supposed to apply to how a compiler determines the size
of an object, and my point was that there are situations where the size
is known only after the initialization has been parsed.

But even that analogy is not very good: in the case of normal arrays,
the initializer determines the *type* of the object, and then the size
follows
from the type in the usual way. In the case of a structure with a FAM,
the type of the object is the struct, and the size of that type does not
include
the array (or at least not necessarily all of it). Since the size of
the object, as reported by sizeof, doesn't include the array either, one
could argue that the array doesn't really belong to the declared object,
but to a
larger unnamed object that contains both the struct and the array.
(That's, BTW, more or less how this extension is explained by GCC
documentation.)

I think the C99 Standard [...]

You do realize that the discussion was not about a standard feature, but
a GCC extension, right?

I wanted to show that according to the C99 Standard (in my interpretation),
it doesn't have to be an extension. I'm sorry if i slightly missed the
intention of this thread.
[...] makes no difference between non-subobjects and
subobjects. It just says

"Each brace-enclosed initializer list has an associated current object.
When no designations are present, subobjects of the current object are
initialized in order according to the type of the current object [...]"

Um... It says "subobjects" right there, and doesn't mention
non-subobjects. Is that not a difference? :)

It says "subobjects ... are initialized ..." here, and in the following text
it says "If an array of unknown size is initialized...". These two
paragraphs fit perfectly together, I think.
Does it? What is the type of the FAM before and after the initializer?

I'm not really sure how I should interpret this wording. The meaning that is
compatible with my reading is: The effective type of the object is an array
type with that many initializers.

That does not, in my reading, necessarily mean that any expression refering
to it (like, a member access) actually has that type too. The aliasing rules
allow an object whose effective type is "T[N]" be accessed by an lvalue of
type "T[]".
Right. But does that mean that an object declared using the GCC
extension is bigger than its type, or does it mean that the declared
object is the size of its type but is a subobject of a bigger, unnamed
object that the compiler allocates to accommodate for the initialized
FAM? The documentation is not completely clear on that, and of course
C99 is silent on the matter.

In the following the object referenced is also larger than the effective
type of a sub-portion of it (at least twice as large, actually).

int *p = malloc(sizeof *p * 2);
*p = 0; // modify the first half only

In the case of "int[]" vs "int[N]", the "int[]" doesn't make any assumption
on the type of the object referenced, and doesn't need to do that either: As
long as you don't invoke sizeof on it, you can perfectly well use such an
expression to modify array elements.

I *think* that any object of size N bytes is also N*(N+1)/2 objects, each
being different because the start address or size of them is different. What
object is addressed depends on the lvalue you use (which in turn determines
the address and effective type, i.e size). Not sure what object is accessed
by an "T[]" expression though - I could imagine that the size-N object is
addressed. Tho if you have any insights, please lemme hear!
 
C

Clive D. W. Feather

Marcin Grzegorczyk said:
(that's 6.7.8p2, not p1)
The constraint is violated because 6.7.2.1p16 says "In most situations,
the flexible array member is ignored", and nothing in the Standard
suggests that initialization is one of the "other" situations. At
least, that seems to be the intent of the rather vague wording.

Perhaps I should have written "except where explicitly stated
otherwise". Sorry.
As an unfortunate consequence of all that, it is impossible to create
portably a static object that could be used as a structure with a
non-empty FAM.

Why "unfortunate"? FAMs are there to legitimize the "struct hack", where
the object is dynamic. If you want a static object with an array at the
end, declare one.
 
C

Clive D. W. Feather

"Johannes Schaub (litb)" said:
It says "subobjects ... are initialized ..." here, and in the following text
it says "If an array of unknown size is initialized...". These two
paragraphs fit perfectly together, I think.

However, the FAM isn't an array of unknown size, it's a syntactic
construct that invokes special behaviour.

At least, that's the intention of what I wrote. The bit about "the size
of the structure" in 6.7.2.1p16 indicates that there's no array there.
 
C

Clive D. W. Feather

Marcin Grzegorczyk said:
Yeah, that's more or less what 6.7.2.1p16 seems to imply.

When I said "the size of an object" above, I meant the size allocated
by the compiler (because that was what the post to which I was
originally replying mentioned), not the size defined by the semantics
of C99. They need not be the same.

Yes, they must.

If I write:
sometype x;

then sizeof(x) equals sizeof(sometype); see 6.5.3.4.

When I write:

int a [] = { 1, 2, 3, 4 };

then this is actually an abbreviation for:

int a [4] = { 1, 2, 3, 4 };

and the above still holds. The compiler is *not* allowed, in that
situation, to create an object whose size is 6 ints long (though, I
accept, it could be hard to determine whether it had).
 
M

Marcin Grzegorczyk

Clive said:
Yes, they must.

Eh, no.
Again, I was referring to the actual implementation. A compiler/linker
obviously can allocate more space for an object than necessary to
represent that object, even if this is rarely (if ever) useful. Of
course, a strictly conforming program has no way of determining if the
implementation does that.
 
M

Marcin Grzegorczyk

Clive said:
Why "unfortunate"? FAMs are there to legitimize the "struct hack", where
the object is dynamic. If you want a static object with an array at the
end, declare one.

I consider it unfortunate because I've come across situations where the
object using the "struct hack" is not at all dynamic (code written for
freestanding implementations without a malloc). If static
initialization of a FAM were a standard feature, in RAM-constrained
systems such objects could be placed in a ROM without sacrificing
portability.

I understand, though, that this feature may be neither widespread enough
(I don't know if any compiler other than GCC supports it) nor useful
enough to warrant its standardization.
 
K

Keith Thompson

Marcin Grzegorczyk said:
Eh, no.
Again, I was referring to the actual implementation. A compiler/linker
obviously can allocate more space for an object than necessary to
represent that object, even if this is rarely (if ever) useful. Of
course, a strictly conforming program has no way of determining if the
implementation does that.

An implementation can allocate as much space as it likes that's
associated with an object, but anything beyond ``sizeof object'' bytes
is, at least in one very important sense, not part of "the size of the
object". It could be thought of as padding or overhead.
 
M

Marcin Grzegorczyk

Keith said:
An implementation can allocate as much space as it likes that's
associated with an object, but anything beyond ``sizeof object'' bytes
is, at least in one very important sense, not part of "the size of the
object".

Yes, I should have used a different wording.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
474,083
Messages
2,570,589
Members
47,211
Latest member
JaydenBail

Latest Threads

Top