From: ctm@ardi.com (Clifford T. Matthews)
Subject: Re: Help requested: Something about pointers still eludes me
Keywords: Pedantic, Strange, True
Organization: ARDI
Date: Thu, 15 Oct 92 19:51:47 GMT

[I originally was going to send this to Lloyd as E-mail, but since a short
 answer wouldn't be sufficient to point out the general answer, I got carried
 away and decided to post this, even though most readers of this group won't
 need the tutorial.  I do believe there are enough NeXT C programmers (even
 C programmers with many years of experience) confused by the issues addressed
 herein that it's worth a post.]

Dear Lloyd and others,

try declaring x as:

	float (*x)[7];

That reads as x is a pointer to one or more arrays of seven floats.
Now x[0] will refer to the first array of seven floats and
    x[0][0] will refer to the first element in the first array of seven floats.

x[a][b] will now do what you want it to.

When you declared x as:

	float **x

You were saying that x was a pointer to a pointer to one or more
floats.  That isn't how you meant to use it; you never did intend
to have other pointers in between your floats and x.

========================== Begin Pedant Mode =================================

The simple rule for reading variable declarations is to remember
that you can add to types to construct more complex types:

	*	pointer to one or more
	[]	array of
	()	function returning

You always read declarations starting where the variable name is
and go from left to right until you've completed everything after
the variable name and then go from right to left until you hit the
type name.  () can be used to alter the left-right/right-left rule,
which in fact is what you need to do when you have a pointer to
one or more arrays of seven floats.

For example, if you had a function that returned such a pointer,
it would be declared thus:

	float	(*ourweirdfunc(void))[7];


You can read this declaration out-loud as:

ourweirdfunc is a function with no arguments (that's what the void
means, you could have other types in there and they would be the
types of the arguments to the function) returning a pointer to one
or more arrays of seven floats.

Which is quite different from an array of seven pointers to functions
(you can't have an array of functions, but you can have an array
of pointers to functions) that return pointers to floats:

	float	*(*ourNEWweirdfunc[7])(void);

Read out-loud you get

ourNEWweirdfunc is an array of seven pointers to functions that
don't have arguments but return pointers to one or more floats.

There's more to be said about variable declarations, but I'm always
amazed that rarely is the consistency of declarations taught when
C itself is taught.  Being able to read declarations won't
automatically help you write them correctly the first time, but it
is a good tool to use when trying to figure out what's going wrong.
By reading your original declaration out-loud:

	float **x;

as x is a pointer to a pointer to one or more floats, you should
at least get a little nervous when you realize that you really
don't intend to have any other pointers between x and the floats.

Another problem is that probably when you were taught (be it through
a class or self-taught) about pointers and arrays, you were never
really taught exactly what they are and how they work.  I don't
have time to go into that in detail, but some added information
might be useful.

When you have an array declared, C will use it in the same way that
it would use a pointer to the same type with the first (left-most)
array subscript stripped off, the only difference being that if
you declare something as a pointer, it's up to you to make it point
to something legit, while when you declare something as an array,
C sets up storage for you.  Compare these array declarations and
type declarations:

	Array				Pointer
int xa[3];			int *xp;
int xaa[5][3];			int (*xap)[3];
int xaaa[7][5][3];		int (*xaap)[5][3];

With those declarations you can say things like

	xp = xa;		/* xp points to the ints stored in xa */
	xap = xaa;		/* xap points to the arrays of 3 ints in xaa */
	xaap = xaaa;		/* xaap points to the arrays of five arrays
					of three ints */

and even

	xap = xaaa[2];	/* This one's obligatorily
				left to the reader to ponder */
				
To get at the ints refered to those beasties you need to add the right number
of subscripts:

	xa[0]		xp[0]
	xaa[0][0]	xap[0][0]
	xaaa[0][0][0]	xaap[0][0][0]

Now we'll talk briefly about mixing and matching pointer dereferences
"*"s and array dereferences "[]"s.  This is probably going to be
too brief, but it should give you something to think about and play
with.


	a[b]

is a shorthand for

	*(a + b)
	
Interesingly enough, "+" is commutative, so you can continue to rewrite a[b] as

	*(b + a)
	
that then begs the question: can we perform the *(x + y) --> x[y]
in reverse to get

	b[a] 

The answer is yes.  If x[3] is a legitimate use of x in C, then
3[x] is also "legitimate" although ill-advised use.  However when
you fully understand how declarations work, the similarities *and
differences* between pointers and arrays and how subscripting works,
you'll never again be haunted by the type of problem that originally
led to this article.  You may also find that you understand and
can properly silence more compiler warnings than you did before
(You *are* compiling with "-O -Wall", aren't you?)

LAST MINUTE WARNING:

C does NOT do range checking for you.  This can bite you whether
you're using subscripting or dereferencing pointers.  Always be
aware of the limits of what you're subscripting or pointing to.

BTW,

The above is a partial synopsis of my method for teaching this
stuff interactively.  A textbook could probably state it better
for home-study, but I don't know of any textbooks that teach this
stuff "properly".

========================== End Pedant Mode =================================

	--Cliff[ord T. Matthews]
	ctm@ardi.com