[comp.lang.c] structure initialization

jeapedai@pttesac.UUCP (J. E. Apedaile) (01/23/90)

The example code that follows compiles correctly and prints the right values
when executed on SunOS 4.0.3 and on a UNIX-PC OS 3.5 (SVR2.?).  The code that
this was taken from also compiled without error on an AT&T SVR2.2 compiler.

Gcc on the UNIX-PC gives the following warning and output:
jim 57 > gcc e.c
e.c: In function main:
e.c:21: warning: excess elements in aggregate initializer
jim 58 > a.out
40, cf000, ff000
0, 0, 0
0, 0, 0
0, 0, 0
0, 0, 0

The code fails to compile at all on an a AT&T 3B20S SVR3.1 or a AT&T 3B4000
SVR3.1.5 and gives the following error output:
cc e.c
"e.c", line 17: too many struct initializers
"e.c", line 17: too many initializers; missing } ?
"e.c", line 17: operands of = have incompatible types
"e.c", line 17: cannot recover from earlier errors: goodbye!

Lint does not complain on any of these systems.

The AT&T compiler and gcc will both compile with out errors if the five
sets of braces surrounding each group of three items in the initialization
are removed.  A quick look through K&R1 did not find anything which would 
lead me to believe that the code as written is wrong.  It seems to me that
forcing the initialization to be 1 group of 15 items instead of 5 groups of
3 items does not make code maintenance easier but harder, especially if the
structure definition and typedef are in a header file.

Why is this no longer allowed?

Any comments?

Jim Apedaile                        	     Work: (415) 545-8300
WORK UUCP: {att,bellcore,sun,ames,pyramid}!pacbell!pttesac!jeapedai
HOME UUCP: {att,bellcore,sun,ames,pyramid}!pacbell!pttesac!clab!jim


 ----------------------------- CUT HERE  -------------------------- 

struct exp1 {
    int length;
    long grp1;
    long grp2;
};

typedef struct {
    struct exp1 blk[5];
} RANGE;


main() 
{
    static RANGE example = {
	{ 40,	0xcf000,	0xff000 },
	{ 50,	0x1cf000,	0x1ff000 },
	{ 60,	0x2cf000,	0x2ff000 },
	{ 70,	0x3cf000,	0x3ff000 },
	{ 80,	0x4cf000,	0x4ff000 },
    };
    register RANGE *rptr;
    register int i;

    rptr = &example;
    for ( i = 0; i < 5; i++) 
	(void)printf("%d, %lx, %lx\n",
		    rptr->blk[i].length,
		    rptr->blk[i].grp1,
		    rptr->blk[i].grp2);
    return(0);
}

-- 
Jim Apedaile                        	     Work: (415) 545-8300
WORK UUCP: {att,bellcore,sun,ames,pyramid}!pacbell!pttesac!jeapedai
HOME UUCP: {att,bellcore,sun,ames,pyramid}!pacbell!pttesac!clab!jim

walter@hpclwjm.HP.COM (Walter Murray) (01/24/90)

J. E. Apedaile writs:

[sample program containing the following declarations]

struct exp1 {
    int length;
    long grp1;
    long grp2;
};
typedef struct {
    struct exp1 blk[5];
} RANGE;
    static RANGE example = {
	{ 40,	0xcf000,	0xff000 },
	{ 50,	0x1cf000,	0x1ff000 },
	{ 60,	0x2cf000,	0x2ff000 },
	{ 70,	0x3cf000,	0x3ff000 },
	{ 80,	0x4cf000,	0x4ff000 },
    };

> The AT&T compiler and gcc will both compile with out errors if the five
> sets of braces surrounding each group of three items in the initialization
> are removed.  A quick look through K&R1 did not find anything which would 
> lead me to believe that the code as written is wrong.  It seems to me that
> forcing the initialization to be 1 group of 15 items instead of 5 groups of
> 3 items does not make code maintenance easier but harder, especially if the
> structure definition and typedef are in a header file.

The problem here is not too many braces, but too few.  You don't have
"5 groups of 3 items", but rather "1 group of 5 groups of 3 items".
The fully-bracketed initialization would look like this:

    static RANGE example = { {
	{ 40,	0xcf000,	0xff000 },
	{ 50,	0x1cf000,	0x1ff000 },
	{ 60,	0x2cf000,	0x2ff000 },
	{ 70,	0x3cf000,	0x3ff000 },
	{ 80,	0x4cf000,	0x4ff000 },
    } };

Note that the initializer begins with three left braces:  one for
example (a struct), one for example.blk (an array), and one for
example.blk[0] (a struct).

It is permitted to elide (omit) some of the braces, but doing so
introduces an ambiguity, depending on whether the compiler parses
the initializer top-down or bottom-up.  Compilers that accept the
example as originally given are probably using a bottom-up parse;
K&R-I and the Standard both require a top-down parse.  A
Standard-conforming compiler is required to produce a diganostic
for the program as originally written, because it looks like you
are trying to initialize 5 members in example, but example has only
1 member.

You are lucky the compiler complained.  There are situations where
the two methods of parsing initializers will yield different
results and no diagnostic.  The only safe approach is to write all
braces, or to omit all but the outermost pair.  Eliding some but
not all is likely to result in portability problems.

> Why is this no longer allowed?

The Standard and its Rationale discuss this at some length.  In the
words of the Rationale, "The Standard has reaffirmed the (top-down)
parse described in the Base Document [K&R-I]."  In K&R-I, reread the
last two paragraphs on page 198.

> Any comments?

I think Andrew Koenig ought to include an example of this in the next edition
of _C Traps and Pitfalls_.

Walter Murray
-------------

maart@cs.vu.nl (Maarten Litmaath) (01/24/90)

In article <1515@pttesac.UUCP>,
	jeapedai@pttesac.UUCP (J. E. Apedaile) writes:
\...
\"e.c", line 17: too many struct initializers
\...
\struct exp1 {
\    int length;
\    long grp1;
\    long grp2;
\};
\
\typedef struct {
\    struct exp1 blk[5];
\} RANGE;
\
\
\main() 
\{
\    static RANGE example = {
\	{ 40,	0xcf000,	0xff000 },
\	{ 50,	0x1cf000,	0x1ff000 },
\	{ 60,	0x2cf000,	0x2ff000 },
\	{ 70,	0x3cf000,	0x3ff000 },
\	{ 80,	0x4cf000,	0x4ff000 },
\    };

Too many initializers indeed!  I think you can figure out why the following
is correct:

    static RANGE example = {{			/* note the `extra' brace! */
	{ 40,	0xcf000,	0xff000 },
	{ 50,	0x1cf000,	0x1ff000 },
	{ 60,	0x2cf000,	0x2ff000 },
	{ 70,	0x3cf000,	0x3ff000 },
	{ 80,	0x4cf000,	0x4ff000 },
    }};						/* ...and its match */
-- 
  What do the following have in common:  access(2), SysV echo, O_NONDELAY?  |
  Maarten Litmaath @ VU Amsterdam:  maart@cs.vu.nl,  uunet!mcsun!botter!maart

bumby@math.rutgers.edu (Richard Bumby) (02/01/90)

In <1515@pttesac>, jeapedai@pttesac.UUCP (J. E. Apedaile) asked about
the behavior of various compilers in initializing a structure.  I
apologize for bothering the net with my response, but I don't trust
our mailer to get to him as none of the links on the printed path
appear to be known.

The code in question is:

struct exp1 {
    int length;
    long grp1;
    long grp2;
};

typedef struct {
    struct exp1 blk[5];
} RANGE;


main()
{
    static RANGE example = {{                 /* brace added */
	{ 40,	0xcf000,	0xff000 },
	{ 50,	0x1cf000,	0x1ff000 },
	{ 60,	0x2cf000,	0x2ff000 },
	{ 70,	0x3cf000,	0x3ff000 },
	{ 80,	0x4cf000,	0x4ff000 },
    }};                                       /* brace added */

.....
}

The extra braces noted enable the compiler on my laptop to compile the
program successfully and give the expected result when executed.
Further insertion of braces gives errors again.  I have yet to try
this on other compilers.  

Is it possible that the typedef introduces the need for an extra level
of bracketing on some compilers?
-- 

--R. T. Bumby ** Math ** Rutgers ** New Brunswick ** NJ08903 ** USA --
  above postal address abbreviated by internet to bumby@math.rutgers.edu
  voice communication unreliable -- telephone ignored -- please use Email

chris@mimsy.umd.edu (Chris Torek) (02/02/90)

In article <Feb.1.09.43.53.1990.19730@math.rutgers.edu>
bumby@math.rutgers.edu (Richard Bumby) writes:
>Is it possible that the typedef introduces the need for an extra level
>of bracketing on some compilers?

No.  Aggregate initialisers always take one level of braces for each
aggregate.  In this case---where we had

	struct exp1 {
	    int length;
	    long grp1;
	    long grp2;
	};
	
	typedef struct {
	    struct exp1 blk[5];
	} RANGE;

an object of type `RANGE' needed a brace because it was a struct,
plus a brace because the (single) element of that struct was an
array; then, each element of the array needed a brace because it
was a struct.

Had the type definition been

	typedef struct {
	    int somevalue;
	    struct exp1 b1;
	    struct exp1 b2[2];
	    int othervalue;
	} RANGE;

this might have been more obvious:

	RANGE x = {
	/* somevalue */		1,
	/* b1 */	{ 1, 2, 3 },
	/* b2 */	{
	/* b2[0] */		{ 4, 5, 6 },
	/* b2[1] */		{ 7, 8, 9 },
	/* b2 */	}
	/* othervalue */	0
	};

If you drop all but the b2/b2[0]/b2[1]/b2 lines, you get

	RANGE x = { { { 4, 5, 6 }, { 7, 8, 9 } } };
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris