[net.lang.c] A last word on arrays? Hah!

rose@think.ARPA (John Rose) (04/16/86)

[ Is this a dead horse or a sleeping dog? ]

Subject:  A last word on arrays? Hah! (LONG)
Summary:
  The inferior status of arrays, and fixes thereto, is ruminated upon.
  Four proposals are presented to the ANSI C committee.
  2000+ lines of array discussion are briefly summarized.

Proposals to ANSI:
	(1) Allow `&' applied to arrays.  Semantics must agree
	    with the implicit addressing which occurs when an array
	    is the first element of a larger array, and the larger
	    array name is used as a pointer.

	    (This is also Kenneth Almquist's proposal, I think.)

	(2) Allow `=' applied to arrays.  Details & rationale below.

	(3) Interpret current C practice of converting array arguments
	    to pointers as passing them by reference; make sizeof()
	    work consistently.

	(4) Allow arrays to be passed as by value.  Details & rationale
	    below.

But first...

Let's get two things very straight:  An array is *not* the same as a
pointer to its first element, and the latter is *not* a pointer to the
array.  These three ideas are all distinct, but in recent mailings they
have been confused in every possible combination:

  array name == 1st elt address:

    From: throopw@dg_rtp.UUCP (Wayne Throop)
    The notational convenience is that an array name "means" the address
    of the first element of the array.

    From: gwyn@BRL.ARPA (VLD/VMB)
    ... the NAME of an array (which means the same as the
    address of the first element of the array)

  array name == array address

    From: mike@peregrine.UUCP (Mike Wexler)
    It just happens that when you put "a" in your code the c compiler
    interprets it as &a.

  array name == array address && array name == 1st elt address:

    From: rh@cs.paisley.ac.uk (Robert Hamilton)
    In the same way (int a[10]) sits in memory somewhere and has an address a
    so you can say int *b=a which also happens to be &a[0].
  
  array address == 1st elt address
    From: PCC-based C compilers
    "tst.c", line 7: warning: & before array or function: ignored

    From: levy@ttrdc.UUCP (Daniel R. Levy) 
    Fortrans implement array references pretty much the same way that C
    does:  by reference to the address of the first element in the array,
    with offsets computed according to the subscripts and then automatically
    dereferenced for use.

Proofs of the existence and distinctness of those entities are easy
to get.  In a second, I'll give a machine-readable proof.  Lucid and
convincing arguments have been given by:

  jsdy@hadron.UUCP (Joseph S. D. Yao) in <322@hadron.UUCP>
  ka@hropus.UUCP (Kenneth Almquist) in <378@hropus.UUCP>

The main stumbling blocks are that
  (1) Arrays per se *almost* always turn into pointers to their first
      elements.  So people are used to thinking of them interchangeably,
      the more so because pointers can be used with what's called ``array''
      syntax.  BUT:  Kenneth Almquist notes that this implicit
      conversion doesn't happen with sizeof, and it could also be
      suppressed with `&'.
  (2) There is a tendency among C programmers to take the type structure
      for granted and think in implementational terms, machine words
      and all that.  So, many reason, since an array name compiles
      into a constant of some sort, that's ``all it is''.  Given
      "short a[2][5]", the addresses of a[0] and a[0][0] evaluate
      to the same machine pointer.  BUT:  Their types (and sizes) are
      different; if they weren't different subscripting would'nt work.

Try this thought experiment:  Suppose all three entities mentioned
above were implicitly interconverted.  Then an array per se gets
turned into the address of its 1st elt.  If that element is in
turn an array, this address can turn to that element per se,
and the cycle repeats, until the array is seen to ``really mean''
the address of the first non-array in the multidimensional aggregrate.
And you've flushed all the type information which would allow indexing
into that aggregate.

Here's code containing all three kinds of quantity.  Run it through your
C compiler.  It works for me on my BSD 4.2 Vax.  I claim that it proves
that PCC-based C compilers distinguish between arrays, their addresses,
and addresses of their elements.

	short two_A[2][5];
		/* two_A[0] is an array-of-short */
	#define A two_A[0] /* short A[5] */
	short (*Ap)[5] = two_A+0; /* == &A */
		/* Ap is a pointer-to-array-of-short */
	short *Ip = A;
		/* Ip is a pointer-to-1st-elt-of-array-of-short */ 
		/* ... a.k.a. pointer-to-short */
	
	main ()
	{
	#define POBJ(x) printf("x as object:  sizeof=%d, sizeof 1st elt=%d\n", \
		sizeof(x), sizeof(*x))
	#define PPTR(x) printf("x as pointer:  addr=%lx, sizeof=%d, incr=%d\n", \
		(long)x, sizeof(x+1), (char*)(x+1) - (char*)(x))
	#define P(x) POBJ(x); PPTR(x)
	P(two_A);
	P(A);
	P(Ap);
	P(Ip);
	}

Joseph Yao also ran similar code on several machines, with the same
conclusion.

Objections of the form ``But why would you ever want to...'' continually
surface in discussions of language-design, and this is no exception.
(((Flame on:  I personally ignore such objections.  They are invariably
based on thoughtless acceptance of existing restrictions.  The language
designer's prime function is not to second-guess details of future
applications, but to provide a symmetrical, easily grasped, powerful
medium for expressing currently-feasible algorithms.  His inquiry
should not be into what current programmers are likely to do, as much as
what current hardware *can* do.  Experience shows that attention to
clean overall design makes a language usable in unforeseen ways.
:Flame off.)))  It has been noted that the address of an array
can be taken implicitly, when that array is an element of a parent
array.  Gregory Smith gave this example:
  Article <2377@utcsri.UUCP> greg@utcsri.UUCP:
	char (*char_ar_ptr)[80];
	char line[80];	/* this is what I want to point it at */
	char_ar_ptr = &line;		/* this should work ... */

To which Chris Torek responded:
  Article <530@umcp-cs.UUCP> chris@umcp-cs.UUCP:
	char (*char_ar_ptr)[80];
	#define N	2	/* e.g. */
	char lines[N][80];
	char_ar_ptr = lines;
  If you only have one line, why do you need to point at a set of lines?

Chris's reasoning seemed to be that the only time you needed to take the
address of an array was when it was inside a bigger array:  Since you
can already take the address implicitly ("lines+1 == /*illegal:*/ &lines[1]")
what's the fuss about?  "Why would you ever want to take the address of
an object which wasn't an array member?"  <=== There it is!  Stomp on it!!
Greg, you needn't be at all embarrassed to admit wanting to take the
address of an object that's not an array element.  Many C programmers
do it.  ("int x, y[5]; foo() { bar(&x), bar(&y[2]); }" :-)

  Article <2439@utcsri.UUCP> greg@utcsri.UUCP
  Good point ... I actually hadn't thought of it exactly that way.
  Two answers come to mind, though:
	  (1) Because it is there. :-)
	  (2) Suppose the array 'lines' is actually more than two:
		  char lines[10][80];
To which I add
	  (3) Since when has taking the address of a lone object
	      become questionable in C??
But given my previous flame, I think that Greg's number (1) alone is
plenty cogent enough to answer the ``Why would you ever want to'' kind
of question.

Greg's final comment is eloquent; please allow a requote:

  So &a[b] is not equivalent to a+b here. What a shame. I like using
  &a[b] in general, and ALWAYS use it when 'a' is an array as opposed to a
  pointer. Too bad I can't... especially when there's no good reason for not
  being able to.

And so, dear reader, the address of an array has a perfectly reasonable
type and value.  But tradition and fuzzy thinking prevent us from being
able to *express* that value, if the array is not an element of an
enclosing array.  (And even then, we must use "(parent_array+4)" and
not the otherwise equivalent "&parent_array[4]".)



I rest my case.  And now may I be so bold as to propose arrays as
lvalues?  By now, no one should b



I rest my case.  And now may I be so bold as to propose arrays as
lvalues?  By now, no one should be saying "Wait!  You can't assign
to a compile-time constant!"  An array's address is a compile-time
constant, but an array per se (unless it is const) is modifiable.

Does this break existing code?  No.  Consider this fragment:

	typedef int MYBUF[3];
	MYBUF a, b;
	a = b;

An array name can currently never appear on the LHS of an assignment.
Now, let's suppose that if one were to appear there, as "a" does,
it is not immediately converted to a pointer, but remains an object
per se, with mutable contents.  The usual conversion of "b" to
pointer is suppressed, and the obvious assignment is performed.

	int *ip;
	ip = b;

This works as before:  "b" suffers the usual conversion.  The
conversion depends how the expression is going to be used.

"Oh no", you gasp.  "This is foreign to C; how will programmers learn
it?"  Well, there's already another set of usual conversions, and
they too are context dependent in exactly the same way.  For example:
	float a, b; double c;
	a = b;
	a = b+0;
	a = b+1;
	c = b;

The first assignment does not convert "b" to double, the second and
third do (on my compiler), and the fourth always does.

Model it this way:  Most operators don't accept floats per se or arrays
per se, and when they are handed one, they apply the "usual
conversions".  But some operators *can* accept the objects unconverted.
Since assignment and `&' currently don't accept *either* arrays
per se or their converted pointers, it is safe to define them
both to operate on the array per se.  (Objectors who assume
the conversion and derive an absurdity are falling prey to
the number (2) stumbling block I mentioned above.)

Am I adding features by allowing `&' and `=' to take array arguments?
No, I'm actually removing them.  The set of types which may be assigned
is currently "all objects with data storage allocated to them, *except*
arrays".  To remove that restriction is to remove a bug/feature of the
language, and make it easier, not harder, to understand C.  Consider
these identities:
	p+n == &p[n]
	(char *)(&x + 1) - (char *)&x == sizeof(x)
They hold true whenever p points to a data object.  "Oops, unless
*p or x is an array."  <== That's a feature, which is worth removing.

Case in point:  Joseph Yao said, in a thoughtless moment:
  Pointers, in C, only point to atomic or aggregate (structure/union)
  objects.
His phrase "atomic or aggregate (structure/union)" attempts to delimit
the set of first-class objects.  Note the clumsiness due to the
exclusion of array-type aggregates.  That mirrors the normal C
programmer's attempt to grapple with the array misfeature.

"But why would you ever want to assign arrays?  Isn't that inefficient?"
That's my business.  I want a language which allows me to express things
the machine can do.  Enclosing the array in a trivial struct will allow
me to assign to around, so why not dispense with the kludge?  If I want
a ``safe'' language, which only allows me to express ``good'' code, I'll
program in Pascal :-{u).  Also, an application has recently been
mentioned for arrays as lvalues:  dan@BBN-PROPHET.ARPA (Dan Franklin)
in <2050@brl-smoke.ARPA> gave plausible code in which jmpbuf objects
need to get assigned.  Many (not all) systems implement jmpbuf as an
array.



Now to questions of arrays as formal parameters.

If arrays are to be assigned (whether by a language primitive or by
bcopy), you need to know their size.  Consider this code:

	foo(x)  int x[5];
	{	return sizeof x;  }

This should return 5*sizeof(int), not sizeof(int*).  Such array
arguments should be treated consistently as *references*, not
*pointers*.  Consult the C++ spec for the distinction.  Even if
references are not included in the ANSI language, the idea is quite
useful.  (In the current net discussion, there has been a lot of mention
of "references", usually synonymous with pointers.  But they are not the
same, if your language has first-class pointers.  Assigning to a
reference bashes the pointed-to object, not the pointer.  Taking the
address of the reference returns the value of the reference, retyped as
a pointer.  Taking sizeof a reference gives the size of the pointed-to
object.)

This is my third proposal to the ANSI committee:  Use Bjarne
Stroustrup's semantics of references for array formal params.  This
would not break code which attempted to use the reference as a constant
pointer, since we have (ref array int) -> (array int) -> (ptr int) all
implicitly.  But it would break code (such as some versions of strcpy)
which attempts to increment the pointer value.  There is a way to get
around this:

	foo(x)  int x[5];
	{	x = x + 1;  }

The expression "x + 1" is a pointer.  The expression on the LHS is a
reference to an array; in all cases this should immediately dereference
to mean the array pointed to by x.  I suggest disabling the dereference
operation, just for formal params, and emitting a warning ("reference
parameter assigned to").  This gets backward compatibility, at the
price of dirtying Bjarne's semantics a bit.  One last objection,
which is fair:  If instead of "x + 1" the RHS were an array, then
the assignment should be of arrays, not pointers.  This could break
existing code.  Perhaps a warning could be emitted:  "using array
assignment on old-style parameter".



By the way, some people have suggested removing structures-as-values
from the language, and having a structure-valued expression immediately
take its own address (except, presumably, in the case of sizeof):

  From: bet@ecsvax.UUCP (Bennett E. Todd III) in <1294@ecsvax.UUCP>
  [Aggregates should not be] "automagically" copied around by the
  compiler; instances of their names as rvalues without explicit
  dereferencing should be converted to pointers in BOTH cases. Note that
  (re)establishing the notion that a structure name evaluates to a
  constant pointer to that structure removes the distinction between the
  "." and "->" operators for structure member dereference, which removes a
  popular source of subtle portability bugs.

This suggestion has been answered by dan@BBN-PROPHET.ARPA (Dan Franklin)
in <2051@brl-smoke.ARPA>, by showing examples (from the Blit software)
of small structs which make great lvalues.  As/if C++ comes into greater
use, the usefulness of this type of programming will become more and
more apparent, and the ``machine register'' school of thought will
perhaps lose the ascendency:

  rbj@icst-cmr (Root Boy Jim) in <2375@brl-smoke.ARPA>
  Fisrt, I disagree that struxure (or array) assignments are A Good Thing.
  I much prefer the model of limiting primitive data types to values that
  can be held in a register on an idealized machine. So much for the
  `religious' part.

Just `religion' on my part.  But there is a serious logical error in
Bennett's posting:  He suggests taking the address of a structure based
on the `precedent' of arrays doing the same thing.  But wait.  We've
seen that before:  An array does not convert to the address of itself,
but of its first element.  So there is really no precedent for
structures.  (Unless we converted them to pointers to *their* first
elements? :-)

However, the `.'/`->' distinction can be annoying.  Suggestions to
merge the two have a valid motivation.  Let me pass on a technique
I stumbled upon, which has saved me lots of debugging time, in
some applications.  (I wouldn't want this to be a language feature,
though.)  Always declare your structures as 1-element arrays.  Wherever
they occur--even as sub-structures.  Then, dispense with dot `.',
and always use arrows.  Voila!  Arrow works everywhere, whether
your LHS names a ``real'' structure or a pointer.  I actually
had a program with hundreds of structure qualifications, about
50/50 split between arrow and dot, which was immediately cleaned
up with this trick.  Downside:  Since arrays are passed by reference,
your structures are too.  But this might be what you want (it was for
me in that particular application).



Now to my fourth and last proposal for ANSI:  Allow arrays per se to be
passed as arguments, and returned from functions, by value.  This is
good for the same reasons as array assignment is:  Implementors of
data abstractions have more choices of the implementation type.
(Please, no ``why would you evers''.  If you place an arbitrary
restriction on programmers--``no assignment of arrays''--they'll
just find a messy workaround, such as a trivial enclosing structure,
and write unclear source code that produces the very same object
code you tried to make impossible.)

This proposal gets close to breaking existing code, since when an array
is passed to a function, it suffers the "usual conversion" to a pointer.
But let's take our cue from the float/double controversy:  Suppress the
"usual conversion" when a prototype is in scope.  For example:

	typedef int MYBUF[3];
	MYBUF a, b;

	MYBUF process_buf(MYBUF c)
	{	/* grovel over c, which is private copy */
		return c;
	}

This is backward compatible, since no old code uses function prototypes.
If you want arrays to be passed by reference, use the old-style function
declaration syntax (or introduce explicit declarations for reference
types!):

	mung_buf_by_reference(c)  MYBUF c;
	{	if (c[2])  --c[2];  }

Calls to functions with no prototypes use the `...' conversions,
which convert an array to a pointer to its first element.  When
the pointer gets to the function, which must be declared in the
old style, it is interpreted (transparently) as a reference, as
per my third proposal above.

The call by reference can also be partially expressed in the new
prototype syntax: 

	mung_buf_by_reference(int * c)
	{	if (c[2])  --c[2];  }

In the latter case, you lose the ``convenience'' of call-by-reference;
sizeof(c) is just sizeof(int *).  Oh no!  No more call-by-reference in
C?  What a loss!--the loss of an extraneous feature:  Only arrays ever
had call-by-reference in the first place.  In other words, the
call-by-reference, so carefully cleaned up in my third proposal, is a
``feature'' of the old-style declarations, which will someday wither
away.  (By then, C will have reference types, and not just for
array-params.)

To summarize:  I've asked for the addition of 4 new features to ANSI C,
which make arrays into first class objects.  I've argued that
programmers can be trusted to use good judgements, and that language
designers should refrain from making them in advance.  I've shown that
the new features actually fill gaps in the old language, and the
resulting language has *fewer* features, since there are fewer
exceptions.  (Next, we go after bitfields.)

			-- John Rose
-- 
----------------------------------------------------------
John R. Rose		     Thinking Machines Corporation
245 First St., Cambridge, MA  02142    (617) 876-1111 X270
rose@think.arpa				  ihnp4!think!rose

gwyn@BRL.ARPA (VLD/VMB) (04/19/86)

jmp_buf jb;

setjmp( jb );

Ok, how do you propose to implement setjmp()?


Allowing array names as lvalues might be worthwhile,
but trying to totally redefine C array behavior at
this point comes under the category of invention,
not standardization.

Contrary to your statements, it is currently correct
to state that in C, with the minor exception of
the operand of sizeof, the NAME of an array object
in an expression is equivalent to a pointer to the
first element of the array.  This holds for
multidimensional arrays too, which is why foo[i]
means what it does for foo declared "type foo[M][N]".
C arrays are certainly not defined as cleanly as most
other elements of the language, but cleaning them up
will break current code.

The reason sizeof array-object is an exception is
that sizeof means "number of bytes of storage
associated with the data object", and if the
exception had not been made, there would be no
reasonable way to say "storage occupied by an entire
array".  I was pretty angry when I found the UTX-32
C compiler claiming that sizeof "xyzzy" was 4 since
that's how big a (char *) is..

Functions are similar in some respects to arrays in
that they have similar name-usage restrictions.

Real problems in C need to be fixed, but the fact
that different types of data have different
properties should not be too upsetting; what would
be the point of different data types otherwise?

Let's get the language standardized as it now
stands and worry about turning it into C++ later.

throopw@dg_rtp.UUCP (Wayne Throop) (04/19/86)

I do have comment on John Rose's proposals.  But first...

John Rose uses a posting of mine as an example of error.  We all know
how painful *that* can be, and I hate it whan that happens.  However, I
would like to point out that John's example is *not* the error he thinks
it is.

> [it is an error to say that]
>   array name == 1st elt address:
> [an example of this error is]
>     From: throopw@dg_rtp.UUCP (Wayne Throop)
>     The notational convenience is that an array name "means" the address
>     of the first element of the array.

The fact that in some cases, as a notational convenience, C takes array
names to mean the address of the first element of the array does *not*
imply that "array name == 1st elt address", and I never said it did.  I
clearly stated that this is a notational convenience, and I think most
people take notational conveniences to be special cases, despite the
fact that this "special case" covers most of the uses of array names.  I
am fully aware, and always have been (well, since first reading K&R
anyhow), that C arrays are not pointers, and array names do not always
"mean" the first element's address.  So there.


On to comment on Rose's proposals.

> [ Is this a dead horse or a sleeping dog? ]

It's a dead dog.  Which has more lives than a barnyard full of cats.

>         (1) Allow `&' applied to arrays.  Semantics must agree
>             with the implicit addressing which occurs when an array
>             is the first element of a larger array, and the larger
>             array name is used as a pointer.

This is fine, this is all well and good.  I object to the fact that it
introduces another glitch.  An accumulation of such glitches makes
things less and less comprehensible, not more and more.  Where we now
have "array names in an expression evaluate to the address of the first
element, except when the object of sizeof", we get something like "array
names in an expression evaluate to the address of the first element,
except when they don't".

In my opinion, the increased confusion of "have I enumerated all the
exceptions yet?" makes the usefulness of the notation questionable, and
in the limit of feeping creaturism, worthless.  Saying "well, it's only
*two* exceptions" doesn't help much, as more are coming in proposals
(2-4), and even this one exception is of questionable use.  I'd rather
have ANSI mandate that a cast of the array name to an
address-of-that-array-type is guaranteed to work.

Don't get me wrong.  I'd *like* to see arrays treated more like other
variables, and be able to "&" and "=" arrays.  In some sense, I am
playing a devil's advocate role here, since I wouldn't be horrified to
see &array allowed.  But the array-is-rvalue convention is so entrenched
that I doubt it would really help, hence my countersuggestion on how to
utter "address-of-array".

>         (2) Allow `=' applied to arrays.

Ugh.  This is much worse.  Now evaluation of an RHS depends on what you
are going to assign it to.  Already the case you say?  Nope, the
conversions that happen for all other cases are conversions that the
assignment implies *after* the evaluation, not a change in the
evaluation strategy itself.  Gag me with an lvalue.

Also, it is prone to horrible errors, like doing an array assignment
when you thought you were assigning the address of an array to a
pointer.  The more the meaning of arrays is muddied, the more common
such errors will become.  Folks already think that a declaration of
an array means they can assign to it like a pointer... at least lint
and the compiler will wake them up.  Making the assignment legal is just
asking for trouble.

>         (3) Interpret current C practice of converting array arguments
>             to pointers as passing them by reference; make sizeof()
>             work consistently.

Well.  Sizeof is indeed peculiar.  For example,
sizeof(formal_array_name) yeilds your machine's pointer size.  Either it
should yeild the array size as it does for other arrays (and complain
about being applied to a a size-ambiguous formal array), or sizeof of
*all* array names should return your machine's pointer size, just as it
would for sizeof(array+0).  This makes sizeof work consistently, and
doesn't do violence to the entrenched indirection/subscription
unification.

In any event, introducing the notion of implicit by-reference passing to
C raises more problems than it solves, for example the question of
whether there should be an implicit dereference of by-reference formals
as there is in other languages.  And the issue of
indirection/subscription unification is inadequately treated by this
proposal.

>         (4) Allow arrays to be passed as by value.
                [by overloading prototype declarations]

Bleah.  Yet another "it acts like x except in cases a, b, and c, and on
fridays and holidays, except on leap-years when it always acts like y
unless you are north of the permanent-frost line" type of construct.
You propose that prototypes act the same as classical declaration,
except for arrays, and except-except-except.  And just how does one
prototype-declare (as opposed to define) the difference between a
function with by-value array formals and one with by-reference array
formals?  As I say, bleah.


> To summarize:  I've asked for the addition of 4 new features to ANSI C,
> which make arrays into first class objects.  I've argued that
> programmers can be trusted to use good judgements, and that language
> designers should refrain from making them in advance.  I've shown that
> the new features actually fill gaps in the old language, and the
> resulting language has *fewer* features, since there are fewer
> exceptions.  (Next, we go after bitfields.)

To criticize: You have asked for the addition of 4 incompletely thought
out additions to ANSI C, which make arrays even more confusing and error
prone than they already are.  You have argued that programmers can be
trusted to use good judgement in the face of overwhelming evidence to
the contrary, and that language designers should refrain from making
needless restrictions in advance, which is true... but language
designers shouldn't be obtuse either.  You have shown that the new
features plaster over some cracks in the old language, at the price of
making things even more confusing, and while some exceptions were
eradicated, more were introduced.

Again, don't get me wrong... I agree with essentially all of what you
state as general principles, and even some of your examples (such as
regularizing sizeof, though I differ in detail).  But I think that the
addition of these particular features to C as it stands today would not
be the benefit you take it to be.

Yet another don't get me wrong... I'm *not* saying that "lowly junior
programmers make poor judgements, unlike myself in my lofty position of
vast seniority".  Rather, I'm saying that I *like* language restrictions
that keep *me* from making mistakes, and I like languages where error is
more easily distinguished from correctness.  It's just that, in my
opinion, these proposals go in the wrong direction, and that to go in
the "correct" direction necessarily implies breaking existing code
(hence, really a new language).

Good luck with bitfields... I've seen languages with *good* provisions
for them, and C ain't one of them.

> John R. Rose    ihnp4!think!rose
--

"Actually, it's a buck-and-a-quarter-quarter staff,
 but I'm not telling *him* that!"
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw

jsdy@hadron.UUCP (Joseph S. D. Yao) (05/03/86)

In article <310@dg_rtp.UUCP> throopw@dg_rtp.UUCP (Wayne Throop) writes:
	[attribution removed]
>I do have comment on John Rose's proposals.  ...
>>         (3) Interpret current C practice of converting array arguments
>>             to pointers as passing them by reference; make sizeof()
>>             work consistently.
>
>Well.  Sizeof is indeed peculiar.  For example,
>sizeof(formal_array_name) yeilds your machine's pointer size.

Ouch.  First off (see that nit?), it took me two (admittedly
fraction-of-a-second) passes to understand that you were talking
about what sizeof yields [sp!] on an array declared as a formal
a r g u m e n t .  I spent at least half a sec wondering how you
dressed your arrays to go formal!  Second, though (if it's a nit,
it's a MONSTER!), the reason it yields one of the pointer sizes
of your machine is that there is no such thing, really, as an
array argument.  It's fairly clear that when you declare a formal
argument to a function, the C compiler coerces it from array-of-X
to pointer-to-X.  You are therefore taking the size of a pointer,
and it returns the size of that pointer (which size may depend on
the type of the thing to which you are pointing).

As usual, this has been discussed to death in earlier articles.
If someone has a copy of my long-ish tutorial on this subject,
please send it to me.  I promise not to re-post it more often than
every year or two, and flag it so you can junk it.
-- 

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