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.