[net.lang.c] double

topher@cyb-eng.UUCP (Topher Eliot) (05/22/86)

(I have dropped net.unix from the newsgroups list).
There has been some debate on what the declaration
	double (*parray[15])[];
means.  David Herron seems to be arguing that it declares a zero-length
array of some sort, which some people and/or compilers might interpret as
being a pointer of the same sort.  I admit I found his note a little hard
to follow, so maybe I misunderstood him.  But I thought I would add my bit
of net volume in the form of an explanation of HOW I would go about parsing
such a statement.  My basic rule, which I have trusted for many years, is
to parse it as if it were an expression, starting inside the parentheses,
and obeying operator precedence rules:

	double (*parray[15])[];
		       ^^^^
1.  It's an array with 15 elements

	double (*parray[15])[];
		^^^^^^^^^^^
2.  It's an array of 15 pointers.

	double (*parray[15])[];
	       ^^^^^^^^^^^^^^^
3.  It's an array of 15 pointers to arrays (the sizes of these arrays being
pointed at is not specified).

	double (*parray[15])[];
	^^^^^^^^^^^^^^^^^^^^^^
4.  It's an array of 15 pointers to arrays of doubles.

There's another way to parse it, sort of from the outside in:

	double (*parray[15])[];
               ^^^^^^^^^^^^^^^
1.  The expression (*parray[15])[] has type double.

	double (*parray[15])[];
               ^^^^^^^^^^^^^
2.  The expression (*parray[15]) has type "array of double", with the size
of the array unspecified.

	double (*parray[15])[];
                 ^^^^^^^^^^
3.  The expression parray[15] has type "pointer to array of double".

	double (*parray[15])[];
                 ^^^^^^
4.  parray has type "array of pointers to arrays of doubles", parray has 15
elements.

Notice that both these methods are nice and methodical, and they both come
up with the same result.

I get the impression that at least one of the participants of this
discussion parsed the declaration something along the lines of:

	double (*parray[15])[];
	                    ^^
1.  It's a zero-length array of somethings.

	double (*parray[15])[];
		^           ^^
2.  It's a zero-length array of pointers to something.

	double (*parray[15])[];
		^^^^^^^^^^^^^^
3.  It's a zero-length array of pointers to 15-element arrays of
something.

	double (*parray[15])[];
	^^^^^^^^^^^^^^^^^^^^^^
4.  It's a zero-length array of pointers to 15-element arrays of doubles.

It is my contention that parsing a declaration this way is just plain
wrong, but I admit it's an easy mistake to make, one that I myself made
back before I had given this subject as much thought as I have.

The original discussion involved lint and malloc; I have nothing to add to
that aspect of it, so I am not repeating it here.

Cheers,
Topher Eliot           Cyb Systems, Austin, TX          (512) 835-2266
 {gatech,harvard,ihnp4,nbires,seismo,ucb-vax}!ut-sally!cyb-eng!topher

friesen@psivax.UUCP (05/24/86)

In article <863@cyb-eng.UUCP> topher@cyb-eng.UUCP (Topher Eliot) writes:
>There has been some debate on what the declaration
>	double (*parray[15])[];
>means.  David Herron seems to be arguing that it declares a zero-length
>array of some sort, which some people and/or compilers might interpret as
>being a pointer of the same sort.
[Topher Eliot's parse]
>
>	double (*parray[15])[];
>		       ^^^^
>1.  It's an array with 15 elements
>
>	double (*parray[15])[];
>		^^^^^^^^^^^
>2.  It's an array of 15 pointers.
>
>	double (*parray[15])[];
>	       ^^^^^^^^^^^^^^^
>3.  It's an array of 15 pointers to arrays (the sizes of these arrays being
>pointed at is not specified).
>
>	double (*parray[15])[];
>	^^^^^^^^^^^^^^^^^^^^^^
>4.  It's an array of 15 pointers to arrays of doubles.
>
	This is indeed correct, and the problem with this declaration
stems from #3. The declaration declares an array of pointers to
entities of *undefined* size(not zero size). Due to the way pointer
arithmetic is defined in "C" this is not kosher(the compiler cannot
determine how much to add to the pointer). At least one compiler I
know of "accepts" this declaration by treating the unspecified arrays
as having size 1, thus making the declaration eqiuvalent to:

	double *parray[15];	/* Array of pointers to double */

Since this is likely what the user really wanted anyway this works
out, but it is still wrong!
-- 

				Sarima (Stanley Friesen)

UUCP: {ttidca|ihnp4|sdcrdcf|quad1|nrcvax|bellcore|logico}!psivax!friesen
ARPA: ??

throopw@dg_rtp.UUCP (Wayne Throop) (05/28/86)

> friesen@psivax.UUCP (Sarima (Stanley Friesen))
>> topher@cyb-eng.UUCP (Topher Eliot)

>>There has been some debate on what the declaration
>>	double (*parray[15])[];
>>means.

> The declaration declares an array of pointers to
> entities of *undefined* size(not zero size).

Right.

> Due to the way pointer
> arithmetic is defined in "C" this is not kosher(the compiler cannot
> determine how much to add to the pointer). At least one compiler I
> know of "accepts" this declaration by treating the unspecified arrays
> as having size 1, thus making the declaration eqiuvalent to:
>
> 	double *parray[15];	/* Array of pointers to double */

No, no, no, no, no!  (But almost yes. :-)

First, the declaration

        double (*a[15])[];

is completely kosher, legal, proper, correct, loyal, honorable, brave,
meaningful, trustworthy, and a fine declaration in its own right.
Harbison and Steele (page 69) say

    The length of the array, a constant expression, may be omitted as
    long as it is not needed to allocate storage.

It does *not* (I say, *NOT*) declare the same thing as

        double *a[15];

nor can any correct compiler for C take these two declarations to be
equivalent.  On the other hand, the expression (a[n]+m) *is*
meaningless, since the size of the denotation type of the pointer
expression a[n] is unknown, which is the problem Stanley points out
above.


Now, how do arrays of unknown size get along with pointer arithmetic?
For array subscription to work, what must be known is the size of the
*components* of the array, or the "inter-element spacing" (digression:
size of an element is *not* the same thing as the inter-element spacing,
but in C the latter can be deduced from the former by fiat (not renault,
mind you, this only works for Italian cars :-)).  The size of the
containing array need not be known, only the lower bound (which is
always zero in C).  Thus, the "innermost" array descriptor (following a
pointer declaration, or in a formal argument or external context) may be
of unknown size.

From this we see that as formal arguments or externals, these
declarations are legal:

        int a[];
        int b[][10];
        int c[][10][10];

and these are not

        int d[10][];
        int e[10][10][];
        int f[10][][10];

and in any context, these declarations are legal

        int (*g)[];
        int (*h)[][10];
        int (*i)[][10][10];

(I will note that, despite the H&S quote above, the legitimacy of the
 (*)[] declaration with unknown size is not totally clear, but most
 compilers and typecheckers side with me on this.)

Now then, the above is mostly the "no, no, no, no, no" part... what
about the "almost yes" part?  Well, it turns out that, due to the
peculiar way pointer/array equivalence works in C, it is impossible to
tell if a pointer-to-foo points to a single foo, or to an element of an
array-of-foo, and therefore if what you want is a pointer which
indicates an array-of-foo, it is often expedient to declare
pointer-to-foo only, and have the array part be implicit via
array-pointer equivalence.  Note well that this notion does *not* make
the two forms identical.  Let's take these definitions:

        int *pi, (*pai)[];

For one example of non-equivalence, to reference "the nth int" via a
pointer to int, one uses

                        *(pi+n)
or, equivalently
                        pi[n]

while with a pointer to array of foo, one uses

                        (*pai)[n]

Uttering "pi" in a value context yields the address of an int (or,
implicitly, the address of the first in an unknown size array of int).
Uttering "pai" in a value context yeilds the address of an array of int.
The distinction between "address of array of int" and "address of the
first element of an array of int" is very important to keep clear.  The
two are definitely *not* the same thing.  (Trust me.  But if you don't,
it might help if you consider what happens when such a pointer is
incremented.)

The brevity of the "pi[n]" notation, and the practical equivalence in
use of this to the "(*pai)[]" form, makes pointer-to-foo the normal way
of talking about pointers to arrays in C.  This does *not* mean that the
distinction between these concepts is absent from C, however.
--
"I would have *sworn* that *somebody* understood arrays in C!"
                                --- overheard in the hallway
--
"'Fiat' is Italian for 'hobby'."
                                --- Eric Hamilton
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw

sam@delftcc.UUCP (Sam Kendall) (05/28/86)

In article <367@dg_rtp.UUCP>, throopw@dg_rtp.UUCP writes:
> (I will note that, despite the H&S quote above, the legitimacy of the
>  (*)[] declaration with unknown size is not totally clear, but most
>  compilers and typecheckers side with me on this.)

So does K&R.  If anybody wants to argue with me on this, I'll find the
section number.  Basically, the statement on empty brackets is this: in
a sequence of contiguous `[ ]' pairs, only the first `[ ]' can be
empty.  But nowhere is it stated that the empty brackets have to be the
first type modifier.  So `type (*)[]' is okay.

----
Sam Kendall			{ ihnp4 | seismo!cmcl2 }!delftcc!sam
Delft Consulting Corp.		ARPA: delftcc!sam@NYU.ARPA