structure containing incomplete array type

E

Eric Smith

Is a structure containing an incomplete array as its last element (per
paragraph 2 of section 6.7.2.1 of ISO/IEC 9899:1999 (E)) itself an
incomplete type? That appears to be indicated by paragraph 22 of
section 6.2.5. If so, that seems to make it difficult to allocate such
structures, because sizeof() is not allowed on incomplete types
(paragraph 1 of section 6.5.3.4).

For instance, I've routinely done things like this:

struct foo { int a; char b; double c []; };

struct foo *alloc_foo (int s)
{
return (calloc (1, sizeof (struct foo) + sizeof (double) * s));
}

But section 6.2.5 seems to make this invalid.

Is this the correct interpretation? If so, is there some other
valid way to implement alloc_foo()?

On the other hand, paragraph 3 of section 6.7.2.3 states that the
declaration of a structure type is complete after the closing brace,
without any caveat about incomplete elements of the structure.

In writing this, I've started to wonder whether it's really specified
that the size of an array of a particular element type is equal to the
product of the array size and the size of the element type. I can't
find anything suggesting that this is required. If it's not, is there
some way given an integer expression to get the size of an array of that
many elements of a particular type? Something like sizeof(double )?

I used to think that using Ada's constrained record types was a hassle,
but now I'm not convinced that the C99 incomplete array in a structure
feature is any better.

Thanks,
Eric Smith

[I'll watch for replies to the newsgroup, but if you want to reply by
email, please remove the obvious spam-proofing from my email address.]
 
J

Jeremy Yallop

Eric said:
Is a structure containing an incomplete array as its last element (per
paragraph 2 of section 6.7.2.1 of ISO/IEC 9899:1999 (E)) itself an
incomplete type? That appears to be indicated by paragraph 22 of
section 6.2.5. If so, that seems to make it difficult to allocate such
structures, because sizeof() is not allowed on incomplete types
(paragraph 1 of section 6.5.3.4).

I think that a struct with a flexible array member has complete type,
although the array has incomplete type. 6.7.2.1#16 covers the
behaviour of sizeof on such structs:

[T]he 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.
In writing this, I've started to wonder whether it's really specified
that the size of an array of a particular element type is equal to the
product of the array size and the size of the element type. I can't
find anything suggesting that this is required.

This follows directly from the definition of an array:

An array type describes a contiguously allocated nonempty set of
objects with a particular member object type, called the element
type.

Since the elements are "contiguously allocated", the size of the array
is simply the sum of the sizes of the elements. This is different
from structs, whose members are merely "sequentially allocated". I
think it follows from the way pointer arithmetic is defined, as well,
but I haven't checked.
If it's not, is there some way given an integer expression to get
the size of an array of that many elements of a particular type?
Something like sizeof(double )?


`sizeof(double)' is valid in C99 (and in C89 if `s' is an integer
constant expression). `double' is a type-name.

Jeremy.
 
E

Eric Smith

Jeremy Yallop said:
I think that a struct with a flexible array member has complete type,
although the array has incomplete type. 6.7.2.1#16 covers the
behaviour of sizeof on such structs:

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

That's what I needed! Somehow I'd overlooked that paragraph.
This follows directly from the definition of an array:

An array type describes a contiguously allocated nonempty set of
objects with a particular member object type, called the element
type.

Ah! So the compiler can't add padding bytes for alignment. I had the
mistaken impression that it was permissible for the compiler to add
padding. For instance, on an architecture with four-byte integers
required to be four-byte aligned, I thought some padding might be used
in a case like this:

struct foo_t { int a; char b; }; /* sizeof(struct foo_t) == 5 */

struct foo_t bar [12];

I'd previously expected that to result in the size of the array being
96 bytes, but based on the array definition you've quoted (6.2.5#20),
apparently it would only be 60 bytes. I suppose the compiler would be
required to generate appropriate code for the misaligned integers, or
to make sizeof(struct foo_t) be a multiple of four.

Thanks!
Eric
 
C

CBFalconer

Eric said:
Jeremy Yallop said:
I think that a struct with a flexible array member has complete
type, although the array has incomplete type. 6.7.2.1#16 covers
the behaviour of sizeof on such structs:

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

That's what I needed! Somehow I'd overlooked that paragraph.
This follows directly from the definition of an array:

An array type describes a contiguously allocated nonempty set
of objects with a particular member object type, called the
element type.

Ah! So the compiler can't add padding bytes for alignment. I
had the mistaken impression that it was permissible for the
compiler to add padding. For instance, on an architecture with
four-byte integers required to be four-byte aligned, I thought
some padding might be used in a case like this:

struct foo_t { int a; char b; }; /* sizeof(struct foo_t) == 5 */

struct foo_t bar [12];

I'd previously expected that to result in the size of the array
being 96 bytes, but based on the array definition you've quoted
(6.2.5#20), apparently it would only be 60 bytes. I suppose the
compiler would be required to generate appropriate code for the
misaligned integers, or to make sizeof(struct foo_t) be a
multiple of four.

You were right the first time. It doesn't pad between array
elements, but the elements themselves are padded so that they
don't need it. That 5 above is probably 8.
 
M

Martin Ambuhl

Eric said:
Is a structure containing an incomplete array as its last element (per
paragraph 2 of section 6.7.2.1 of ISO/IEC 9899:1999 (E)) itself an
incomplete type? That appears to be indicated by paragraph 22 of
section 6.2.5.

"Appears to indicated"? If explicit words only "appear to indicate,"
something is terribly wrong:
[#23] ... A structure type
containing a flexible array member is an incomplete type
that cannot be completed.

If so, that seems to make it difficult to allocate such
structures, because sizeof() is not allowed on incomplete types
(paragraph 1 of section 6.5.3.4).

Did you not read in that same section 6.7.2.1
[#15] 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, and 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.97) When an lvalue whose type is a structure with a
flexible array member is used to access an object, it
behaves as if that member were replaced with the longest
array, with the same element type, that would not make the
structure larger than the object being accessed; the offset
of the array shall remain that of the flexible array member,
even if this would differ from that of the replacement
array. If this array would have no elements, then it
behaves as if it had one element, but the behavior is
undefined if any attempt is made to access that element or
to generate a pointer one past it.


For instance, I've routinely done things like this:

struct foo { int a; char b; double c []; };

struct foo *alloc_foo (int s)
{
return (calloc (1, sizeof (struct foo) + sizeof (double) * s));
}

But section 6.2.5 seems to make this invalid.

Did you not read in that same section 6.7.2.1
[#16] EXAMPLE Assuming that all array members are aligned
the same, after the declarations:

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

the three expressions:

sizeof (struct s)
offsetof(struct s, d)
offsetof(struct ss, d)

have the same value. The structure struct s has a flexible
array member d.

[#17] If sizeof (double) is 8, then after the following code
is executed:

struct s *s1;
struct s *s2;
s1 = malloc(sizeof (struct s) + 64);
s2 = malloc(sizeof (struct s) + 46);

and assuming that the calls to malloc succeed, the objects
pointed to by s1 and s2 behave as if the identifiers had
been declared as:

struct { int n; double d[8]; } *s1;
struct { int n; double d[5]; } *s2;

[#18] Following the further successful assignments:

s1 = malloc(sizeof (struct s) + 10);
s2 = malloc(sizeof (struct s) + 6);

they then behave as if the declarations were:

struct { int n; double d[1]; } *s1, *s2;

and:

double *dp;
dp = &(s1->d[0]); // Permitted
*dp = 42; // Permitted
dp = &(s2->d[0]); // Permitted
*dp = 42; // Undefined behavior
Is this the correct interpretation?

For someone who quotes section numbers of the standard, you seem to have
done precious little in the way of reading it.
 
J

Jeremy Yallop

Martin said:
Eric said:
Is a structure containing an incomplete array as its last element (per
paragraph 2 of section 6.7.2.1 of ISO/IEC 9899:1999 (E)) itself an
incomplete type? That appears to be indicated by paragraph 22 of
section 6.2.5.

"Appears to indicated"? If explicit words only "appear to indicate,"
something is terribly wrong:
[#23] ... A structure type
containing a flexible array member is an incomplete type
that cannot be completed.

Those words don't appear in the standard, although they do in draft
versions. I believe them to be false as a statement about C99.
Did you not read in that same section 6.7.2.1

The words you quote have also been changed in the standard and it
would be unwise to rely upon them.
Did you not read in that same section 6.7.2.1
[#16] EXAMPLE

The text of these examples has been changed between the final public
draft and the actual standard.
For someone who quotes section numbers of the standard, you seem to have
done precious little in the way of reading it.

"n869" is not the standard.

Jeremy.
 
K

Kevin Bracey

In message <[email protected]>
CBFalconer said:
Eric said:
Ah! So the compiler can't add padding bytes for alignment. I
had the mistaken impression that it was permissible for the
compiler to add padding. For instance, on an architecture with
four-byte integers required to be four-byte aligned, I thought
some padding might be used in a case like this:

struct foo_t { int a; char b; }; /* sizeof(struct foo_t) == 5 */

struct foo_t bar [12];

I'd previously expected that to result in the size of the array
being 96 bytes, but based on the array definition you've quoted
(6.2.5#20), apparently it would only be 60 bytes. I suppose the
compiler would be required to generate appropriate code for the
misaligned integers, or to make sizeof(struct foo_t) be a
multiple of four.

You were right the first time. It doesn't pad between array
elements, but the elements themselves are padded so that they
don't need it. That 5 above is probably 8.

Indeed, but as we were originally talking about flexible array members, it's
worth noting one possible implementation quirk:

struct foo_t { int a; char b; }; /* sizeof(struct foo_t) == 8 */
struct jim_t { int a; char b; char c[1]; }; /* sizeof(struct jim_t) == 8 */
struct bar_t { int a; char b; char c[]; } /* sizeof(struct bar_t) == 5 */

That's quite possible, and a logical way to implement flexible array members.
(The alternative would be to add 3 bytes of padding between b and c in
bar_t). Structures with flexible array members cannot be elements of arrays,
so trailing padding is not needed to ensure alignment of the int. Our
implementation works in this way.

FWIW, this is one of gcc's non-conformances to C99 - its sizeof(struct bar_t)
== 8, but offsetof(bar_t, c) == 5.
 

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
473,774
Messages
2,569,599
Members
45,165
Latest member
JavierBrak
Top