[comp.lang.c] multi-d arrays and types

rpjday@ccu.umanitoba.ca (03/26/90)

  Consider the following snippet of program, involving
a 2-d array for a calendar.

  int calendar[12][31];
  int (*monthptr)[31];

Clearly, calendar is a 2-d array in the sense that ANYTHING 
in C is a 2-d array, since all arrays in C are technically
1-dimensional.  Based on my understanding of arrays in C,
what calendar is is a 1-d array, whose elements themselves
are arrays of length 31 of integers.  Right so far?  OK.

The variable "monthptr" is now declared as a pointer to 
an array of length 31 of int, which SHOULD match with
the type of element of the array calendar, no?  Based on
this, I write a loop to scan the elements of calendar,
the outermost loop looking like

  for (monthptr = calendar; monthptr <= calendar[11]; monthptr++)
  {
    .....
  }

My assumption is that the first assignment should not be a
problem, as we have a pointer being assigned the name of an array
whose elements have EXACTLY the same type as the pointer.
This, in fact, works.  "monthptr++" also works as address
arithmetic increments monthptr by the length of an int[31].

The weirdness is in the comparison which, while it works,
complains that the types are incompatible ("warning: illegal
pointer combination").  Putting an "&" in front of "calendar[11]" 
makes it even worse as the compiler complains that I now have
"& before array or function", tosses the "&", then generates
the first warning.  The question is, just what the heck type 
is "calendar[11]"????  

It seems to me that what I am seeing is some sort of schizophrenic
behaviour.  "calendar[11]", on the one hand, is an element of
an array, so I should be allowed to apply "&" to it.
On the other hand, it is itself an array so I shouldn't need
the "&" in front of an array.  Making things explicit and
using "(int (*) [31]) calendar[11]" finally makes the compiler
stop complaining, but this doesn't get me any closer to figuring
just what type this is, if it isn't already an "int (*) [31]".
Comments?

karl@haddock.ima.isc.com (Karl Heuer) (03/27/90)

In article <1990Mar26.155319.23986@ccu.umanitoba.ca> rpjday@ccu.umanitoba.ca writes:
>  int calendar[12][31];
>  int (*monthptr)[31];
>  for (monthptr = calendar; monthptr <= calendar[11]; monthptr++) ...
>[Assignment and increment are okay, but the comparison has a problem.]

Since "calendar" has type "array of array of int", "calendar[11]" has type
"array of int", which decays into "pointer to int", which does not match
the "pointer to array of int" on the left side.  What you really want is
"&calendar[11]", but...

>Putting an "&" in front of "calendar[11]" makes it even worse as the compiler
>complains that I now have "& before array or function", tosses the "&", then
>generates the first warning.

Unfortunately, in pre-ANSI C it was not legal to explicitly take the address
of an array.  (This was a botch; the relationship between arrays and pointers
was not as well defined in the old days.)  Your options include: [0] use an
ANSI C compiler; [1] patch your compiler to remove (nothing to add!) the line
that makes this illegal; [2] rewrite your code with a dummy struct around the
array; [3] (recommended) change the expression from "&calendar[11]" to
"calendar+11", which is equivalent (due to the definition of "[]") but works
right even in pre-ANSI C.

Oh, I also recommend that you use 12 rather than 11 as the limiting subscript,
and change the relational operator to a strict "<".

Karl W. Z. Heuer (karl@ima.ima.isc.com or harvard!ima!karl), The Walking Lint

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

In article <1990Mar26.155319.23986@ccu.umanitoba.ca>
rpjday@ccu.umanitoba.ca writes:
>  int calendar[12][31];
>  int (*monthptr)[31];
>Clearly, calendar is a 2-d array in the sense that ANYTHING 
>in C is a 2-d array, since all arrays in C are technically
>1-dimensional.  Based on my understanding of arrays in C,
>what calendar is is a 1-d array, whose elements themselves
>are arrays of length 31 of integers.  Right so far?  OK.

Right.

>The variable "monthptr" is now declared as a pointer to 
>an array of length 31 of int, which SHOULD match with
>the type of element of the array calendar, no?

An element of `calendar' has type (int [31]); `monthptr' has
type `(int (*)[31])', so: no.

>Based on this, I write a loop to scan the elements of calendar,
>the outermost loop looking like
>  for (monthptr = calendar; monthptr <= calendar[11]; monthptr++)

The rule is:

    In a value context, an object of type `array N of T' (for any
    constant N and any suitable type T) turns into a value of type
    `pointer to T', whose value is the address of the first (0'th)
    element of the array.

The first part is OK:

	monthptr = calendar;
  =>	<object, ptr to array 31 of int, monthptr> =
		<object, array 12 of array 31 of int, calendar>;

Now apply the rule: N=12, T=`array 31 of int':

  =>	<object, ptr to array 31 of int, monthptr> =
		<value, ptr to array 31 of int, &calendar[0]>;

>My assumption is that the first assignment should not be a
>problem, as we have a pointer being assigned the name of an array
>whose elements have EXACTLY the same type as the pointer.
>This, in fact, works.  "monthptr++" also works as address
>arithmetic increments monthptr by the length of an int[31].

Yes.

>The weirdness is in the comparison which, while it works,
>complains that the types are incompatible ("warning: illegal
>pointer combination").

Because they are:

	monthptr <= calendar[11]
  =>	<object, ptr to array 31 of int, monthptr> <compare>
		<object, array 31 of int, calendar[11]>

(I have skipped a few steps here but the idea should be clear.)  N=31,
T=`int':

  =>	<object, ptr to array 31 of int, monthptr> <compare>
		<value, ptr to int, &calendar[11][0]>

>Putting an "&" in front of "calendar[11]" makes it even worse as the
>compiler complains that I now have "& before array or function", tosses
>the "&", then generates the first warning.

This simply means you have an old (`Classic C') compiler rather than an
ANSI (`New C') compiler.

>The question is, just what the heck type is "calendar[11]"????

It *is* type `array 31 of int'; it *becomes* `pointer to int' because it
is used where a value is needed.

>It seems to me that what I am seeing is some sort of schizophrenic
>behaviour.

This is the reason (or one of the reasons) that ANSI C allows the `&'.

The solution to the whole problem is to replace

	monthptr <= &calendar[11]	/* correct in ANSI C only */

with

	monthptr <= calendar + 11	/* correct in ANSI & old C */

which has the same effect but does not invoke the unnecessary restriction
on `&' in old C compilers.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@cs.umd.edu	Path:	uunet!mimsy!chris

ark@alice.UUCP (Andrew Koenig) (03/27/90)

In article <1990Mar26.155319.23986@ccu.umanitoba.ca>, rpjday@ccu.umanitoba.ca writes:

>   Consider the following snippet of program, involving
> a 2-d array for a calendar.

>   int calendar[12][31];
>   int (*monthptr)[31];

This example looks a lot like the one that starts on
page 30 of `C Traps and Pitfalls,' so let me quote the
comment that ends the discussion of that example:

	At this point we have walked far enough out on the
	ice that we had better turn back before falling
	through; although this last example is valid ANSI C,
	I had trouble finding a compiler that would accept it.

If you don't have an ANSI compiler, and a pretty good one,
you're likely to be out of luck.
-- 
				--Andrew Koenig
				  ark@europa.att.com