C declarations

jsdy at SEISMO.ARPA jsdy at SEISMO.ARPA
Wed Feb 13 16:10:02 AEST 1985


Becuse of all the verbiage, I had mailed this only to the original
poster of the message.  It now seems that confusion is more rampnt
than I had believed.  I'm therefore going to post this publicly.
(*sigh*)

>                                       ...   The [] notation is equivalent
> to the * notation, right?

Wrong.

A pointer [int *ip;] is a unit of memory whose contents will be the
address of that thing to which it points.  It initially points to --
well, nothing in particular.  If you use a pointer, you must put the
address of an existing (or allocated) object into it, first.  That's
why it's an error to take all the UNIX Section 2 function declarations
literally -- they all use pointer notation, but some of them (read,
write, stat, e.g.) really need a real object to act on.

An array (int ia[N];) is actually N real objects of the type of which
you have the array.  (Did I say that right?)  So, in this case, if
N == 4, then I have just reserved space for 4 int's.  The real objects
that exist, here, are ia[0], ia[1], ia[2], and ia[3].  The symbol "ia"
here refers to no single existing object.

So, now comes the confusing part.  "Ia" doesn't refer to any existing
object -- so, for instance, an attempt to say:
	ia = new_value;
gets an error from C.  But if we use "ia" as a pure value, it appears
to have a value which is the address of the first element of the array
(often put, "the address of the array").  We can then do pointer arith-
metic with this value!  In this way, it  a p p e a r s  to be (but is
not) a pointer.  Just to be reciprocal, if we now set
	ip = ia;
and try to access ip[0], we find to our delight that it works the other
way around:  the pointer can  a p p e a r  to be the pure address of
the array (first element of ...).  In fact, the pointer still is a
memory unit containing said address.

Just to confuse things a little bit more, there is one case in which
all distinctions are lost.  A little history:  in early C compilers,
there was no way in which anything larger than an int (or maybe a long)
could be passed as arguments to functions.  [Pointers at that time were
considered to be about the size of one or another int.  On a really
abstract machine, this may or may not be true.  It is not true on one
poorly designed family of microprocessor: the 80*86.]  However, people
wanted to pass arrays.  "No problem," says the lone language designer,
"we'll just pass the pure value which is the address of the array."

			PRESTO.

Whether you pass an array name or a pointer to a function, the argument
will be a pointer.  Many people first learn this with main():
	main(argc, argv, envp)
	 int argc;
	 char **argv;
	 char **envp;
	{
	}
is exactly identical to:
	main(argc, argv, envp)
	 int argc;
	 char *argv[];
	 char *envp[];
	{
	}
Within the functions, they are really pointers, no matter how you
declare them; and they behave entirely like them.  They are separate
words of storage containing the address of the arrays of (char *)'s
or strings that are, respectively, the arguments and environment
variables.  However, it is still true that for any other automatic
declarations and for all static and external declarations, the
distinction between array and pointer remains as I have said.  (As far
as I know, no implementation of C allows register arrays -- but register
pointers are the greatest thing since sliced bits.)

> 	int ptr[]   <=>   int *ptr
> 	int *ptr[]  <=>   int **ptr

Hopefully, you now understand that the upper left and upper right items
are not equivalent, unless they are function arguments.  The UL item
declares that somewhere there is a set of objects (int's), while the
UR object is a single unit of memory pointing off to one or a sequence
of said objects.  Note that the pointer can point to the first element
of an array ("to the array"), and then be incremented by 1 to point to
the next element of the array, no matter how big the object in the array
"actually" is.  Thus the popular notion that the pointer to an object
may also be a pointer to an array of said objects.

The LL item, of course, is an array of pointers to strings.  The '[]'
operator binds more closely than the '*' operator.  (The only case I can
think of offhand where a binop is tighter than a unop.)  The LR item is
a pointer to a pointer to an int.  Of course, the pointer that it points
to may be the first in an array of pointers!  Thus:
	ptr ->  _____  -> (int)
		_____  -> (int)
		...
And it must never be confused with either int iaa[M][N]; or int *(iap[]);
the former of which is an actual 2-D array, and the latter of which is a
pointer to a single array of int's!  Think about it:  in the first,
iaa[M] is an array of N int's: so you get:
	int 0, int 1, ..., int N-1,	(0 row)
		...
	int 0, int 1, ..., int N-1,	(M-1 row)
or M * N int's closely packed together!  In the second, you have a unit
of memory which is the address of a series of int's in a row.  Gee, you
might as well have said int *iap; for all the goos that does you.  (I am
hedging the truth here -- that notation is sometimes useful.)

> 	int ptr[];	declares one pointer

I'm sure you see the problem with this, now.  In fact, with no subscript
and no initialiser, this is a zero-length array!

> 	int ptr[] = { 1, 2, 3 };	declares a three element int array.
Yup!

Mnmmm ... it's getting late.  Any further questions will have to be
deferred until the next class.  [;-)]  [Go ahead & write if you want.]

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



More information about the Comp.lang.c mailing list