Address of array

Joseph S. D. Yao jsdy at hadron.UUCP
Sat Mar 22 18:49:50 AEST 1986


In article <428 at batcomputer.TN.CORNELL.EDU> garry%geology at cu-arpa.cornell.edu.arpa writes:
>In a recent article jsdy at hadron.UUCP (Joseph S. D. Yao) wrote:
>>...  There is no such thing as a pointer to the whole
>>array:  that is a Pasqualische or Fortranian notion.  Pointers, in
>>C, only point to atomic or aggregate (structure/union) objects...
>
>Are you sure of this? I sometimes write:
>	foo (ap) register float (*ap)[4][4]; {... (*ap)[0][0] = 33; ...}
>I do this because my compiler (DEC/Vms) ends up making slightly better
>use of registers than if I wrote:
>	foo (array) float array[4][4]; {... array[0][0] = 33; ...}
>(and because it's slightly more pleasing to my brain actually to say "pointer-
>to-array" if that's what I'm thinking of). Are you saying my syntax is legal
>only by the grace of DEC?
>
>garry wiegand
>garry%cadif-oak at cu-arpa.cs.cornell.edu

garry @ cadif-oak or geology:

You bring up a good point, and one that I hadn't thought to mention.
You are in fact using a pointer to a matrix!  Your compiler probably
makes different code because you don't say register float array[4][4];
in the second one.  (Try it!) I don't understand why your compiler and
lint don't complain about your code, though.  These two pieces of code
are in fact not equivalent in their declarations, though they are the
same in effect, as I will explain below.  By the way, there are (lots
of!) known problems with VMS "C", in terms of what it does versus what
all other "C" compilers do.  DEC does document most of these; I am not
at all sure how thouroughly.

The good point is this.  Whenever you pass the address of an array,
of  a n y   d i m e n s i o n , as an argument to a function, it comes
out on the other side as a pointer.  As I mentioned in the last
article, this pointer can be looked at as pointing to an object of
atomic type, no matter how deeply dimensioned the original array,
because all the array is, is a set of objects next to each other in
memory.  There are no pointers or complex aggregate objects in that
memory.  This is one way (the machine's way) of looking at it.
Another way might be to consider each level of dimensioning an
aggregate:  so, an array of int [7] is seven ints in a row; and a
matrix (2-d array) of int [5][7] is an array of 5 (array of 7 ints
in a row)s in a row.  Then x[3][4] is the 3*7+4th or 25th int in
the row.  And then, x is that elusively-sought object, the address
of [i.e., a pointer to] an array of 7 ints.  BUT, at the same time
it is the address of the first int, and the address of an array of
35 ints, and the address of a 5 X 7 matrix of ints!  How do we tell
the difference?

At this point, it might be worth sitting down and drawing a picture
of the array, both in X-Y form:
	x[0][0], x[0][1], ... , x[0][6],
	x[1][0], ...		x[1][6],
	...
	x[4][0], ...		x[4][6]
and in memory-address order:
	_x:	x[0][0]
	_x+4:	x[0][1]
		...
	_x+136:	x[4][6].

Now, obviously, if I coerce x to be an int array, I can also index it
from 0 to 34.  As mentioned above, whenever one uses an array name as
an argument to a function, it gets coerced into a pointer, and must be
declared correctly on the other side.  For instance, int **x; would be
WRONG here: why? ...
...
Because, as we said before, there are no pointers here, as there
would have to be to have a pointer to a list of pointers.  Similarly,
int *x[] is WRONG:  this also declares a list of pointers, and is
equivalent to int **x.  The declarations int *x; and int x[]; are
also equivalent to each other; and, while they may be used instead
of the truth (indexed, as I said, 0 - 34), they are not the truth.

Now, int x[5][7]; is the truth.  This can, by the way, also be written
as int x[][7]; without the compiler objecting, since the compiler does
not need to know the first subscript.  After all, you can have any
number of 7-int arrays pointed to by the passed pointer.  Since, as
stated above, int *x; and int x[]; are the same, then can anyone see
why int x[][7]; and int (*x)[7]; shouldn't be the same?  I thought
not ...

In fact, they are the same.  Much to my surprise, we have found the
elusive pointer to an array!  If I declare int (*x)[7]; I should be
able to increment x and get it to point to the next row of seven ints.
Well, hush my puppies, it does exactly that!  (Cc on 4.2BSD on ISI
68000 Q-bus machine.  Cc on Xenix 2.8b on 8086 Altos 986.  Cc on
Ultrix 1.1 on VAX 11/750.  These are the closest machines I can get
to on one hop.  All agree.)  And, in fact, this falls out logically
from the language description -- it just is a little-used turn of
phrase (as it were).  So, what does x[3][4] mean in this context?
It means index x by 3, to get the third further row of 7 ints from
the one pointed to by x; then take the 4th further int in that row.
Or, the 25th int, just as before!

Now, why is garry's code wrong?  Well, just as we can have a 1-d
alias for a 2-d array, so can we have a 3-d alias.  And that is
exactly what garry has created for us.  The declaration: int
(*x)[5][7]; is the same as int x[][5][7].  The use of this, as
(*x)[3][4], is the same as x[0][3][4].  How do we translate this?
Well, x is a pointer to a number of sequential 5X7 matrices.  In
this sequence of matrices, we are selecting the 0*35+3*7+4th int,
or the 25th int.  Therefore, the effect is the same, even though
we are saying that we are using a 3-d array!  In fact, we can take
this to as many dimensions as we want.

Yes, Virginia, this is confusing.  I'm sorry.  As I explained last
time, it has a little to do with the power of the language, and a
lot to do with history.  To change it would break a whole lot of
existing C programs, which is one of the things that ANSI X3J11 is
trying not to do (except for #endif	label's, grr grr).  And so
would should tune our mental models accordingly and learn to live
with it.

By the way:  I can understand not having lint to tell one that the
declaration and call in the C program have distinctly different
declarations.  VMS has been trying to catch up with UNIX in terms
of software tools, and seems to be falling rapidly behind.  But
surely, if you passed an &'d array to a function, a compiler error
message something like:
"tst.c", line 7: warning: & before array or function: ignored
should have come out!  Doesn't it?  That should tell you something!

Disclaimer:  any misteaks in the past 130 or so lines are purely
the result of my home workstation bottling up this message and
squirting it out past midnight so that it looks and feels like
I wrote it in my sleep ...  The C did compile the way I described,
though.  No denying it.  Pointers to arrays LIVE!
-- 

	Joe Yao		hadron!jsdy at seismo.{CSS.GOV,ARPA,UUCP}



More information about the Comp.lang.c mailing list