[net.lang.c] multidimensional arrays in C

dgary@ecsvax.UUCP (D Gary Grady) (01/05/85)

<>
Sorry to belabor this point, but judging from the mail I'm getting, many
people are still very confused about multidimensional C arrays.  I'll
try to be brief, and I hope this substitutes for individual responses to
my mail (I have had trouble reaching several sites).

A declaration of the form

int x[3][5];

allocates 30 bytes of storage (assuming 2 bytes per integer).  These 30
bites can be considered 3 adjacent arrays of 5 integers each.  No
pointers to these 3 arrays of integers are created.  In a reference of
the form

foo = x[2][4];

(say), the offset of the appropriate element in x is computed by the
expression

x + 2*sizeof(x[0]) + 4*sizeof(x[0][0]);

where the [0] could be replaced by any subscript.  Note that
sizeof(x[0][0]) is 2: the size of an integer.  More germane is the fact
that sizeof(x[0]) is 10, or the length of a row in bytes.  The important
point is that C _must_ know about that [5] in the declaration in order
to perform this calculation.

In a subroutine, the declaration

foo(x)
int x[][];

CANNOT be used to pass an array declared as above (x[3][5], I mean).  If
your compiler accepts this parameter declaration (a few won't), it will
treat it as identical to

foo(x)		or		foo(x)
int **x;	equivalently	int *x[];

which simply cannot produce correct results for an attempt to pass good
old x.  It IS possible to create an array of pointers to arrays and use
int **x or its variants to get at the elements.  It is also possible to
access elements of a conventional two-dimensional array passed as a
parameter by treating it as a one-dimensional array and doing the
subscript calculation "by hand."  And, of course, one can specify the
dimension sizes in the subroutine (optionally omitting the first one).

The problem is that you can't write a subroutine that handles two (or
higher) dimensional arrays without knowing either coding in constant
dimensions or doing the subscript calculation by hand.  That is a very
annoying limitation to anyone trying to hawk C as an alternative to
FORTRAN (which handles variable-sized arrays quite simply).

If I have still not managed to convince the folks who mail me K&R quotes
without knowing what they mean (come on: :-)), I have a suggestion:
Let's see you do it.  Write the subroutine that will print out the
elements of this matrix, one row per line, WITHOUT using a constant
dimension and WITHOUT treating the passed array as one-dimensional.  I
don't think it can be done.  Here's a main program:

extern print_it();		/* Note your routine is external! */

main()
{
int x[2][3] = {{1, 2, 3}, {4, 5, 6}};	/* Subject to change */

print_it(x,2,3);
}

Best,

-- 

D Gary Grady
Duke University Computation Center, Durham, NC  27706
(919) 684-4146
USENET:  {decvax,ihnp4,akgua,etc.}!mcnc!ecsvax!dgary

dgary@ecsvax.UUCP (D Gary Grady) (01/05/85)

<>
Sorry to belabor this point, but judging from the mail I'm getting, many
people are still very confused about multidimensional C arrays.  I'll
try to be brief, and I hope this substitutes for individual responses to
my mail (I have had trouble reaching several sites).

A declaration of the form

int x[3][5];

allocates 30 bytes of storage (assuming 2 bytes per integer).  These 30
bites can be considered 3 adjacent arrays of 5 integers each.  No
pointers to these 3 arrays of integers are created.  In a reference of
the form

foo = x[2][4];

(say), the offset of the appropriate element in x is computed by the
expression

x + 2*sizeof(x[0]) + 4*sizeof(x[0][0]);

where the [0] could be replaced by any subscript.  Note that
sizeof(x[0][0]) is 2: the size of an integer.  More germane is the fact
that sizeof(x[0]) is 10, or the length of a row in bytes.  The important
point is that C _must_ know about that [5] in the declaration in order
to perform this calculation.

In a subroutine, the declaration

foo(x)
int x[][];

CANNOT be used to pass an array declared as above (x[3][5], I mean).  If
your compiler accepts this parameter declaration (a few won't), it will
treat it as identical to

foo(x)		or			foo(x)
int **x;	equivalently		int *x[];

which simply cannot produce correct results for an attempt to pass good
old x.  It IS possible to create an array of pointers to arrays and use
int **x or its variants to get at the elements.  It is also possible to
access elements of a conventional two-dimensional array passed as a
parameter by treating it (within the subroutine) as a one-dimensional
array and doing the subscript calculation "by hand."  And, of course,
one can specify constant dimension sizes in the subroutine (optionally
omitting the first one).

The problem is that you can't write a subroutine that handles two (or
higher) dimensional array parameters without either coding in constant
dimensions or treating them as funny one-dimensional arrays.  That is a
very annoying limitation to anyone trying to hawk C as an alternative
to FORTRAN (which handles variable-sized arrays quite simply).

If I have still not managed to convince you, I have a suggestion:
Let's see you do it.  Write the subroutine that will print out the
elements of this matrix, one row per line, WITHOUT using a constant
dimension and WITHOUT treating the passed array as one-dimensional.  I
don't think it can be done.  Here's a main program:

extern print_it();		/* Note your routine is external! */

main()
{
int x[2][3] = {{1, 2, 3}, {4, 5, 6}};	/* Subject to change */

print_it(x,2,3);
}

Best,
-- 

D Gary Grady
Duke University Computation Center, Durham, NC  27706
(919) 684-4146
USENET:  {decvax,ihnp4,akgua,etc.}!mcnc!ecsvax!dgary

Doug Gwyn (VLD/VMB) <gwyn@Brl-Vld.ARPA> (01/06/85)

I love C and use it for almost all applications programming.
The single biggest deficiency I have found in the language
is what Mr. Grady explained:  Formal array parameters are
not permitted to have parametric dimensions.  That is,

void mat_mul( p, q, r, a, b, c )
	int	p, q, r;	/* dimensions */
	double	a[p][q], b[q][r], c[p][r];	/* arrays */
	{

is not a legal C procedure header.  It is clear that a C
compiler COULD be made to handle this more general case,
and that there is no practical way to code this procedure
using the current language definition.  If one wants to
displace Fortran from its dominating position in numerical
software, this problem HAS to be solved.

chris@umcp-cs.UUCP (Chris Torek) (01/06/85)

I would like to point out (or perhaps belabour the obvious) that
FORTRAN can't access multidimensional arrays without knowing the
dimensions either.  If you write

	SUBROUTINE FOO (A, N)
	INTEGER N
	REAL A(N,N)

	INTEGER I, J
	REAL V
	...
	V = A(I,J)
	END

then the compiler computes the array as (pseudo C)

	v = *(a + (j-1)*n + i-1);	/* N.B.: this is NOT optimized */

(assuming I haven't screwed up my row & column order, as usual).

It takes JUST AS MUCH MACHINE TIME (though more programmer time*) to
access "ordinary" multi-dimensional arrays in C as it does in FORTRAN.
(And don't talk to me about your fancy optimizing compiler which
generates pointers to vectors; I can do that in C too. :-))

----------------
*This IS a consideration---but how much programmer time is wasted
finding bugs like DO 10 I = 1.20?
-- 
(This line accidently left nonblank.)

In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (301) 454-7690
UUCP:	{seismo,allegra,brl-bmd}!umcp-cs!chris
CSNet:	chris@umcp-cs		ARPA:	chris@maryland

Doug Gwyn (VLD/VMB) <gwyn@Brl-Vld.ARPA> (01/08/85)

No, parametric formal array parameter dimensions would not
break any existing code, since the only dimensions now permitted
are integer constants which have the same meaning as they would
under my proposal.  There would also be no ambiguity in the
meaning of such type declarations.

It is true that a section of the compiler would have to be done
somewhat differently to support this feature.