[comp.lang.c] Question about type casting

boemker@hpfcdc.HP.COM (Tim Boemker) (01/10/88)

What's wrong with this program?

main()
{
	int *i;

	int j[1];

	((int [1]) i) [0] = 1;
}

gwyn@brl-smoke.UUCP (01/12/88)

In article <5080009@hpfcdc.HP.COM> boemker@hpfcdc.HP.COM (Tim Boemker) writes:
-What's wrong with this program?
-main()
-{
-	int *i;
-	((int [1]) i) [0] = 1;
-}

How can you turn a pointer into an array?  It makes no sense.
Just use i[0] = 1; which is the same as *i = 1;.

boemker@hpfcdc.HP.COM (Tim Boemker) (01/13/88)

> In article <5080009@hpfcdc.HP.COM> boemker@hpfcdc.HP.COM (Tim Boemker) writes:
> -What's wrong with this program?
> -main()
> -{
> -	int *i;
> -	((int [1]) i) [0] = 1;
> -}
> 
> How can you turn a pointer into an array?  It makes no sense.
> Just use i[0] = 1; which is the same as *i = 1;.

Consider int A[1].  A is semantically an array, but the expression "A" is a
pointer to the base of that array.  The only differences between a pointer
and array are (1) when the storage is allocated and (2) how index calculations
are made.

Using i[i] instead of *i may be fine if the array in question is one
dimensional, but it doesn't work exactly the same way when the array
is multidimensional.  Consider int *A.  If I know that A points to
a 10x20 array, I might like to address elements as A[i][j].  I can
do the indexing math myself, as in *(A + i * 20 + j), but that's not
what the question is really about.  I want to know why a syntactically
correct and semantically meaningful expression is not allowed.

If you need further provocation, consider this program:

main()
{
  int A[1];

  ( (int [1]) A ) [0] = 0;
}

My C compiler doesn't like that construction, either, presumably for the
same reason that it didn't like the one in the first example.  But please
explain to me why it's illegal to cast an expression into it's natural
type!  Why can't I cast an array as an array?

throopw@xyzzy.UUCP (Wayne A. Throop) (01/13/88)

> boemker@hpfcdc.HP.COM (Tim Boemker)
> What's wrong with this program?
>    main() {
> 	int *i;
> 	int j[1];
> 	((int [1]) i) [0] = 1;
>    }

Lint has this to say about it:

    t17.c
    ==============
    (4)  illegal lhs of assignment operator
    (4)  warning: i may be used before set
    (3)  warning: j unused in function main
    (5)  warning: main() returns random value to invocation environment

In addition to the minor faults, that no exit value was provided, j was
unused, and i was used before it was initialized, lint claims that the
lhs of the assignment operator is illegal.  Which isn't quite right, in
that it really should have complained about the cast.

But even if we accept the dubious legality of the cast, which is
justified only by very broad interpretation of K&R, (how do you convert
a pointer-valued expression to an array-valued expression, anyway?), the
resulting expression being subscripted would have to be a temporary
array (with, for this reason, no aliases), and the whole assignment
becomes a monumentally obfuscated no-op.  (In particular, it does *NOT*
by any remote stretch of the imagination initialize i.)

But clearly K&R did not intend to allow anything to be cast to array
type, since casts are defined as conversions, and the allowable
conversions do not include any that result in arrays.

--
I think it's time to stop carping on the blunders of the President and
give him some credit for creativity.  I mean, where do you even FIND a
Jewish hard-line conservative Republican pot-smoker?
                                        --- A. Whitney Brown
-- 
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw

boemker@hpfcdc.UUCP (01/13/88)

> But even if we accept the dubious legality of the cast, which is
> justified only by very broad interpretation of K&R, (how do you convert
> a pointer-valued expression to an array-valued expression, anyway?), the
> resulting expression being subscripted would have to be a temporary
> array (with, for this reason, no aliases), and the whole assignment
> becomes a monumentally obfuscated no-op.  (In particular, it does *NOT*
> by any remote stretch of the imagination initialize i.)

> But clearly K&R did not intend to allow anything to be cast to array
> type, since casts are defined as conversions, and the allowable
> conversions do not include any that result in arrays.

1. I'm not so sure about K&R's intentions.  True, a cast is a conversion, but
   the book does not explicitly define the set of legal conversions; it only
   lists conversions.  The list is not necessarily complete.

2. Whenever an array identifier appears in an expression, it is treated as
   being a pointer to the first element of the array.  Casting an expression
   as an array is just asking that expression to be used as a pointer to the
   base element of a (mutli-dimensional) rectangular collection of elements.
   Using an expression of type array is defined, and the object referenced
   is not the whole collection of elements but a pointer to the collection
   (pp. 195 bottom and 210 top).  Saying that the resulting expression would
   have to be a temporary array does not make sense under K&R, and it shows
   a misunderstanding of its concept of arrays: every array expression of
   the form A[i] is equivalent to the pointer expression *(A+i).

chris@mimsy.UUCP (Chris Torek) (01/14/88)

In article <546@xyzzy.UUCP> throopw@xyzzy.UUCP (Wayne A. Throop) writes:
[regarding the cast]
>> 	((int [1]) i) [0]

>But clearly K&R did not intend to allow anything to be cast to array
>type, since casts are defined as conversions, and the allowable
>conversions do not include any that result in arrays.

Or, to put it another way, the result of a cast is an rvalue, and
C does not have array rvalues.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

boemker@hpfcdc.UUCP (01/15/88)

> I believe that Standard C does not allow type casting on the left of the 
> = sign.
 
How about this:

int i;
char *c;

c = (char *) &i;

* (int *) c = 0;

Are you saying that Standard C doesn't allow c to be recast in that assignment?

> I have also discovered that SUN and APOLLO C allow this while most other C
> compilers do not???!

So the Sun accepts constructions like ((int [1]) i) [0] = 0; ?
Does it work?

chris@mimsy.UUCP (Chris Torek) (01/15/88)

In article <5080010@hpfcdc.HP.COM> boemker@hpfcdc.HP.COM (Tim Boemker) writes:
>... Consider int *A.  If I know that A points to a 10x20 array,

Given that declaration, A cannot point to a 10 x 20 array.  It
*can* point to the first element of the first row of such an array
[see example below], from which all other element addresses can be
computed; but it cannot point to the entire array.  This is precisely
the difference between a pointer to an object and a pointer to an
array N of objects: only the latter points to the entire array.

>I might like to address elements as A[i][j].  I can
>do the indexing math myself, as in *(A + i * 20 + j), but that's not
>what the question is really about.  I want to know why a syntactically
>correct and semantically meaningful expression is not allowed.

Because, given the way C is defined, it is not semantically
meaningful!  One small (?) tweak---allow entire arrays to be
rvalues---and it becomes meaningful, but it is not now so.

>Why can't I cast an array as an array?

Because rvalues cannot be arrays, and the result of a cast is an
rvalue.  Rvalues *can* be *pointers* to arrays:

	int *A, i, j;
	...
	/* here we decide (by fiat or by analysis) that A points to the
	   first element of X[10][20], i.e., A == &X[0][0], and then we
	   set X[i][j] through A: */
	((int (*)[20])A)[i][j] = 1;

---although this has the limitation that all but the first dimension
must be known at compile time, and again, this is precisely the
same limitation as the one that exists for formal array parameters.

--------

Note also that the cast replaces the first `array N of' with `pointer
to'.  This is the general rule that reduces arrays to pointers in
C expressions (except as arguments to sizeof).  If we decide that
A points to the first element of `int Z[10]', and we want to set
Z[5], we can write this:

	((int (*))A)[5] = 999;

but this reduces to

	((int *)A)[5] = 999;

or just

	A[5] = 999;

so this is really rather boring.
-- 
In-Real-Life: Chris Torek, Univ of MD Comp Sci Dept (+1 301 454 7163)
Domain:	chris@mimsy.umd.edu	Path:	uunet!mimsy!chris

gwyn@brl-smoke.ARPA (Doug Gwyn ) (01/15/88)

In article <5080010@hpfcdc.HP.COM> boemker@hpfcdc.HP.COM (Tim Boemker) writes:
>  int A[1];
>  ( (int [1]) A ) [0] = 0;
>Why can't I cast an array as an array?

The second `A' above is the NAME of an array.  In this context it gets
converted right away into a pointer to its first element.  You cannot
legitimately convert a pointer into an array.

throopw@xyzzy.UUCP (Wayne A. Throop) (01/18/88)

> boemker@hpfcdc.HP.COM (Tim Boemker)
> I want to know why a syntactically
> correct and semantically meaningful expression is not allowed.
> If you need further provocation, consider this program:
> main()
> {
>   int A[1];
>   ( (int [1]) A ) [0] = 0;
> }

The question is ill formed.  The above cast, while it *is* syntactically
correct, is *NOT* semantically meaningful.  The real question is why
were the rules that give C syntax its semantics chosen in such a way as
to make that cast meaningless.  For a full answer, one would have to ask
dmr, but the paraphrase of the question Tim asks can be addressed:

> Why can't I cast an array as an array?

To review the rules: casts take the value of the target expression and
convert this value to a new type.  An array, when it appears in an
expression, evaluates (yields a value) with pointer type, not array
type.  Thus, the cast of what appears to be something to its own type
is, in fact, an attempt to convert a pointer to an array, not an attempt
to cast an array as an array.

Now, why were these rules chosen?  Well, the rule that really gets in
the way here is the rule about how arrays are to be treated when
evaluated.  This special treatment of arrays in its turn was based on
the unification of array subscripting and address arithmetic in C.  In
other words, it is the indirect consequence of an attempt to make C a
simpler and more conceptually unified language.  In retrospect, I think
better tradeoffs could have been made, but I don't myself pretend to be
a genius language designer, so I tend to accept C as it is, with only
modest regrets.  

--
Giving up on assembly language was the apple in our Garden of Eden:
Languages whose use squanders machine cycles are sinful.  The LISP
machine now permits LISP programmers to abandon bra and fig-leaf.
                                                --- Alan J. Perlis
Moon calls them Fortran Machines and Lisp Machines,
but I would prefer to call them fuzzy mammals and
pterodactyls myself.
                                                --- Richard P. Gabriel
-- 
Wayne Throop      <the-known-world>!mcnc!rti!xyzzy!throopw

tim@amdcad.AMD.COM (Tim Olson) (01/18/88)

In article <5080012@hpfcdc.HP.COM> boemker@hpfcdc.HP.COM (Tim Boemker) writes:
| > I believe that Standard C does not allow type casting on the left of the 
| > = sign.
|  
| How about this:
| 
| int i;
| char *c;
| 
| c = (char *) &i;
| 
| * (int *) c = 0;
| 
| Are you saying that Standard C doesn't allow c to be recast in that assignment?

I believe that the previous poster really meant that C doesn't allow
*lvalues* to be cast.  In your example, 'c' is not an lvalue, but '*c'
is.  Therefore, you can write

	* (int *) c = 0;
	
but not

	(int)(*c) = 0;


	-- Tim Olson
	Advanced Micro Devices
	(tim@amdcad.amd.com)
	

gwyn@brl-smoke.ARPA (Doug Gwyn ) (01/18/88)

In article <5080011@hpfcdc.HP.COM> boemker@hpfcdc.HP.COM (Tim Boemker) writes:
>2. Whenever an array identifier appears in an expression, it is treated as
>   being a pointer to the first element of the array.

I can think of two exceptions to this: when the array name is the operand of
sizeof or the operand of &.

>   Casting an expression
>   as an array is just asking that expression to be used as a pointer to the
>   base element of a (mutli-dimensional) rectangular collection of elements.

How do you figure that?  A cast operator is supposed to accomplish a virtual
assignment of the operand into an object of the specified type, an array in
this case, and we should all know that C does not support assignment into an
array.

>   Saying that the resulting expression would
>   have to be a temporary array does not make sense under K&R, ...

Excuse me, but saying anything else does not make sense.