[net.lang.c] C array follies 1

throopw@rtp47.UUCP (Wayne Throop) (09/04/85)

All right.  Put on your C array thinking caps.

Consider the following program:

    void f(x)
        int x[2][2][2];
    {
        printf( "%o %o %o\n", x, x[0], x[0][0] );
        printf( "%d %d %d\n", sizeof(x), sizeof(x[0]), sizeof(x[0][0]) );
    }
    void main(){
        int x[2][2][2];
        printf( "%o %o %o\n", x, x[0], x[0][0] );
        printf( "%d %d %d\n", sizeof(x), sizeof(x[0]), sizeof(x[0][0]) );
        f(x);
    }

Clearly the printfs are "illegal", but assume you have a fairly vanilla
machine.  Eight-bit bytes, four-byte ints.  Four-byte pointers.
Pointers and ints stored in the same way for argument passing.

Two questions:
  - What does this program print?
    (Especially what is printed on the last output line?)
  - Is this "correct"?  (Again, especially the last line.)

I think that most systems will print "4 16 8" (or equivalent) as the
last line.  While this is (probably) not a bug, I think it is at least a
misfeature.  This same point was part of the "C bites" topic, which is
what drew my attention to it.  The point is *should* C bite in this way,
and if so, why?
-- 
Wayne Throop at Data General, RTP, NC
<the-known-world>!mcnc!rti-sel!rtp47!throopw

gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>) (09/07/85)

>     void f(x)
>         int x[2][2][2];
>     {
> ...
>     }
>     void main(){
>         int x[2][2][2];
> ...
>         f(x);
>     }
> ...
> I think that most systems will print "4 16 8" (or equivalent) ...

Yes, you are not actually passing an entire array to the function f() but
rather just a pointer to a 2x2 array of ints.  C lets you misdeclare this
	void f( int x[2][2][2] );	/* ANSI C assumed */
which is the real misfeature.  The "correct" declaration is technically
	void f( int (*x)[2][2] );
which also is supported by C (thankfully), or if you prefer,
	void f( int x[][2][2] );

Fortunately, old C did not have this misfeature for structs/unions,
so it was possible to add to C the ability to pass struct arguments
in addition to just pointers to them.  For reasons of backward
compatibility, it is unfortunately NOT possible to change C so that
	f( x );
actually passes the array and not just a pointer to the first element.

Maybe language "D" can fix all C's mistakes (ha).  Meanwhile we just
have to learn to cope with them.

anton@ucbvax.ARPA (Jeff Anton) (09/07/85)

In article <171@rtp47.UUCP> throopw@rtp47.UUCP (Wayne Throop) writes:
>Consider the following program:
>
>    void f(x)
>        int x[2][2][2];
>    {
>        printf( "%o %o %o\n", x, x[0], x[0][0] );
>        printf( "%d %d %d\n", sizeof(x), sizeof(x[0]), sizeof(x[0][0]) );
>    }
>    void main(){
>        int x[2][2][2];
>        printf( "%o %o %o\n", x, x[0], x[0][0] );
>        printf( "%d %d %d\n", sizeof(x), sizeof(x[0]), sizeof(x[0][0]) );
>        f(x);
>    }
>Clearly the printfs are "illegal", but assume you have a fairly vanilla
>machine.  Eight-bit bytes, four-byte ints.  Four-byte pointers.
>Two questions:
>  - What does this program print?
>    (Especially what is printed on the last output line?)
>  - Is this "correct"?  (Again, especially the last line.)
>
>I think that most systems will print "4 16 8" (or equivalent) as the
>last line.  While this is (probably) not a bug, I think it is at least a
>misfeature.  This same point was part of the "C bites" topic, which is
>what drew my attention to it.  The point is *should* C bite in this way,
>and if so, why?
>-- 
>Wayne Throop at Data General, RTP, NC
><the-known-world>!mcnc!rti-sel!rtp47!throopw

sizeof returns the size of the object.  Since arrays are passed as addresses
which are put into pointer vars, sizeof(x) in the called function should be
the sizeof the pointer.  Are you proposing that C should pass the dimentions
of the array also so a called function can know these things?  Or maybe
since the pointer was declared as 'int x[2][2][2];' you want sizeof x to
be 8 ints?  I prefer to keep C's ability to pass pointers to arrays or
subarrays.  A use of sizeof often has the address taken also so returning
the size of the pointer not the array it points to prevents a careless
programmer from stomping over memory.  As far as debugging goes, I
hate large memory updates that might run to far or not far enough.
They are probably the must tricky bugs around.  If you know the size of
an array because you know its dimensions, you might as well put a
#define XSIZE 2*2*2*sizeof(int)
in near your declaration.
	Perhaps it might be nice to pass array dimensions by arguments
to function calls.
-- 
C knows no bounds.
					Jeff Anton
					U.C.Berkeley
					Ingres Group
					ucbvax!anton
					anton@BERKELEY.EDU

root@bu-cs.UUCP (Barry Shein) (09/09/85)

>From: gwyn@brl-tgr.ARPA (Doug Gwyn <gwyn>)
>Subject: Re: C array follies 1
>
>Maybe language "D" can fix all C's mistakes (ha).  Meanwhile we just
>have to learn to cope with them.

"The C Programming Language", Ritchie et al, BSTJ V57, No. 6, Part 2, 1978,
page 2019:

	"...C would probably have to be left as is, and a
	totally now language developed. We leave it to the
	reader to speculate whether it should be called
	D or P." (very last sentence in the article.)

For your amusement.

		-Barry Shein, Boston University
Spoiler:

To prevent 10**27 'why P?'s: first BCPL, then B, then C...(P ... L...)

throopw@rtp47.UUCP (Wayne Throop) (09/10/85)

> > [Original article questions practice of having sizeof an array
> > formal always return pointer size]

> ... Are you proposing that C should pass the dimentions of the array
> also so a called function can know these things?  Or maybe since the
> pointer was declared as 'int x[2][2][2];' you want sizeof x to be 8
> ints?

I am suggesting that the latter notion is a good idea.  In particular,
the formal declaration
        int x[3];
should make sizeof(x) == (sizeof(int) * 3).  The formal declaration
        int *x;
should make sizeof(x) == sizeof(int *).  The formal declaration
        int x[];
is a problem.  I would prefer it if sizeof(x) yeilded a diagnostic.
Failing that, sizeof(x) == sizeof(int *) is next best, I suppose.

> I prefer to keep C's ability to pass pointers to arrays or subarrays.

So do I.  The above suggestion does not affect this.  It only affects
what happens when sizeof is applied to such a formal name.  I dislike
the incongruity that in this code fragment

        void f(x) int x[3]; { int y[3]; ... }

sizeof(x) != sizeof(y).  This seems like a misfeature to me.
-- 
Wayne Throop at Data General, RTP, NC
<the-known-world>!mcnc!rti-sel!rtp47!throopw

guy@sun.uucp (Guy Harris) (09/12/85)

> I am suggesting that the latter notion is a good idea.  In particular,
> the formal declaration
>         int x[3];
> should make sizeof(x) == (sizeof(int) * 3).

An alternative is simply to forbid such a formal declaration.

	foo(x)
	int *x;
	{
		x++;
	}

would, if "x" pointed to the "i"th element of an array of "int"s, cause "x"
to point to the "i+1"st element of that array.

	foo(x)
	int x[3];
	{
		x++;
	}

would cause great confusion, since

	foo()
	{
		int x[3];

		x++;
	}

is illegal.

C doesn't permit you to pass arrays by value, so I think

	foo(x)
	int x[3];
	{
		...

is meaningless.  If C permitted you to take the address of an array other
than a subarray,

	foo(x)
	int (*x)[3];
	{
		...

might be meaningful and useful (it says that "foo" takes a pointer to an
array with 3 "int" elements - not 4, not 2, but 3 and only 3).  However, you
can't take the address of such an array, so

	bar()
	{
		int y[3];

		foo(&y);
	}

is illegal.

C's "declaration syntax == usage syntax" breaks down when it comes to arrays.

	int a[3];

	a[1] = 1;
	*(a + 2) = 2;

is legal, and would also be legal if "a" were declared as

	int *a;

However, the code, while still legal C, would mean something very different
in the second form.

Since a declaration of a formal argument is, not surprisingly, a
declaration, and since the declarations "int a[];" and "int *a;" are
inequivalent if "a" is not a formal argument, I don't think array
declarations and pointer declarations should be equivalent for formal
arguments.  Since it is nonsensical to declare an array formal, it should be
made illegal or, at least, deprecated - too much code uses "int a[];" as a
synonym for "int *a;" when declaring a formal to simply forbid it.

	Guy Harris

throopw@rtp47.UUCP (Wayne Throop) (09/14/85)

> > the formal declaration
> >         int x[3];
> > should make sizeof(x) == (sizeof(int) * 3).
>
> An alternative is simply to forbid such a formal declaration.
> ...
>         foo() { int x[3]; x++; }
> is illegal.  [and thus by analogy the formal should be made illegal]
>                       Guy Harris

I agree that this is better than the current situation.  Interestingly
enough, while lint raises not a peep in the following example for b, our
local typechecker has something different to say about this:

    1   void f(a,b)
    2       int *a, b[];
    3   {
    4       int *c, d[1];
    5       ++a;
    6       ++b;
    7       ++c;
    8       ++d;
    9   }

    #1017 6  not an lvalue (:IDENTIFIER b :AUTO ... )
    #1017 8  not an lvalue (:IDENTIFIER d :AUTO ... )

therefore, my code already obeys this restriction.  I also try to
remember to declare formal arrays as pointers always.  However, I
still think that if formal arrays are to be allowed, they "ought to"
have the sizeof properties I outlined before.
-- 
Wayne Throop at Data General, RTP, NC
<the-known-world>!mcnc!rti-sel!rtp47!throopw