[comp.lang.c] Multi-dimensional array initialization

schmidt@siam.ics.uci.edu (Doug Schmidt) (06/11/89)

I have a question concerning multi-dimensional array initalization.

Consider the following 2-dimensional array declaration:

----------------------------------------
typedef struct { int i; int j; } bar;

static bar foo[4][4]; /* 4 * 4 == 16 element array of `bar's. */
----------------------------------------

Now, let's assume that this array is given 16 initial values instead:

----------------------------------------
typedef struct { int i; int j; } bar;

static bar foo[4][4] =
{
  { {1,0},  {2,0},  {3,0},  {4,0}, },
  { {5,0},  {6,0},  {7,0},  {8,0}, },
  { {9,0}, {10,0}, {11,0}, {12,0}, },
  {{13,0}, {14,0}, {15,0}, {16,0}, },
}; 
----------------------------------------

Everything works as expected, the initializer has 4 explicitly
demarcated rows with 4 elements in each row.

Now, suppose that we leave out the explicit curly braces that demarcate rows,
i.e., certain '{' and '}':

----------------------------------------
static bar foo[4][4] =
{
  {1,0},  {2,0},  {3,0},  {4,0},  {5,0},  {6,0},  {7,0},  {8,0},
  {9,0}, {10,0}, {11,0}, {12,0}, {13,0}, {14,0}, {15,0}, {16,0},
};
----------------------------------------

There are still 16 array cells, and 16 initializer groups.

However, this initialization gives different results depending on
whether it is compiled with GCC 1.35 or Sun OS 4.0.1's cc compiler The
only difference is the absence of surrounding '{' '}' to demarcate
each row in the initializer for the two dimensional array `bar.'

  Can someone please tell me which version is *correct* w.r.t.

1. The latest ANSI-C draft.
2. Traditional behavior on UNIX compilers.

  thanks very much,
  
    Doug

Here's a complete program that illustrates the problem:

----------------------------------------
typedef struct
{
  int i;
  int j;
} bar;

#ifndef FIXBUG
static bar foo[4][4] =
{
  {1,0},  {2,0},  {3,0},  {4,0},  {5,0},  {6,0},  {7,0},  {8,0},
  {9,0}, {10,0}, {11,0}, {12,0}, {13,0}, {14,0}, {15,0}, {16,0},
};
#else
static bar foo[4][4] =
{
/* Note the extra { } for each row... */

  { {1,0},  {2,0},  {3,0},  {4,0},},
  { {5,0},  {6,0},  {7,0},  {8,0},},
  { {9,0}, {10,0}, {11,0}, {12,0},},
  {{13,0}, {14,0}, {15,0}, {16,0},},
};
#endif

main ()
{
  int i,j;
  
  for (i = 0;i < 4; i++)
    {
      for (j = 0; j < 4; j++)
        printf ("%4d", foo[i][j].i);
      printf ("\n");
    }
}

----------------------------------------


--
Any man's death diminishes me,              | schmidt@ics.uci.edu (ARPA)
Because I am involved in Mankind;           | office: (714) 856-4043
And therefore never send to know for whom the bell tolls;
It tolls for thee        -- John Donne

curtw@hpcllca.HP.COM (Curt Wohlgemuth) (06/13/89)

schmidt@siam.ics.uci.edu (Doug Schmidt) writes:

> Consider the following 2-dimensional array declaration:
> 
> ----------------------------------------
> typedef struct { int i; int j; } bar;
> 
> static bar foo[4][4]; /* 4 * 4 == 16 element array of `bar's. */
> ----------------------------------------
> 
> Now, let's assume that this array is given 16 initial values instead:
> 
> ----------------------------------------
> typedef struct { int i; int j; } bar;
> 
> static bar foo[4][4] =
> {
>   { {1,0},  {2,0},  {3,0},  {4,0}, },
>   { {5,0},  {6,0},  {7,0},  {8,0}, },
>   { {9,0}, {10,0}, {11,0}, {12,0}, },
>   {{13,0}, {14,0}, {15,0}, {16,0}, },
> }; 

In this example, all of the braces for the initializer are specified;
none are elided.  Every compiler should properly initialize 'foo' in the
same manner here.

> Now, suppose that we leave out the explicit curly braces that demarcate rows,
> i.e., certain '{' and '}':

I.e., you want to partially elide the braces for the initializer for 'foo' --
a dangerous action!

> ----------------------------------------
> static bar foo[4][4] =
> {
>   {1,0},  {2,0},  {3,0},  {4,0},  {5,0},  {6,0},  {7,0},  {8,0},
>   {9,0}, {10,0}, {11,0}, {12,0}, {13,0}, {14,0}, {15,0}, {16,0},
> };
> ----------------------------------------
> 
>   Can someone please tell me which version is *correct* w.r.t.
> 
> 1. The latest ANSI-C draft.

The December '88 draft standard (and I assume this is unchanged) says (3.5.7):

	"If the initializer of a subaggregate or contained union begins 
	with a left brace, the initializers enclosed by that brace and 
	its matching right brace initialize the members of the 
	subaggregate or the first member of the contained union."

In the second example above, according to the (d)pANS then, the first '{' 
begins the initializer for 'foo'.  The next '{' begins the initializer for
the first subaggregate for 'foo': namely, 'foo[0]'.

So we have a sub-problem:  '{1,0}' initializes 'foo[0]', _in its entirety_.
Similarly, '{2,0}' initializes 'foo[1]', '{3,0}' initializes 'foo[2]',
and '{4,0}' initializes 'foo[3]'.

Now the compiler sees '{5,0}', but it has run out of storage to fill
(it tries to put it into 'foo[5]').  Hence some kind of diagnostic
for "too many initializers".

All of this is because you elided some braces in the initializer.  Had you
left them in (as in the first example), then
		'{ {1,0}, {2,0}, {3,0}, {4,0} }'
would be the total initializer for 'foo[0]', and you would get what you want.
Similarly, if you elide _all_ the braces (except the outermost) in the
initializer, you will also get what you desire.

> 2. Traditional behavior on UNIX compilers.

Typically, pcc-based compilers seem to parse brace-enclosed initializers
in a bottom-up fashion.  ANSI specifies that it must be done in a top-down
manner (which, I believe, K&R intended also).  Hence the discrepency you 
observe.