Casting a postdecrement operand

Wayne Throop throopw at dg_rtp.UUCP
Sat Jun 7 07:28:38 AEST 1986


> greg at utcsri.UUCP (Gregory Smith)

> I stand corrected. I couldn't find anything explicitly forbidding or
> allowing this in K&R, ( I still can't ) so I gave that compiler the
> benefit of the doubt, without trying it here.

For those interested, here are the places that make

        ((T *)P)++

illegal.  First, in K&R, two places.  In the reference section, 7.2,
unary operators:

    An expression preceeded by the parenthesized name of a data type
    causes conversion of the value of the expression to the named type.
    This construction is called a cast.

Note that the *value* is *converted*.  K&R are careful to call lvalues
lvalues, and the note that a cast works on the value of the expression
is noteworthy.  Note also that this value is *converted*.  It is *not*
"taken as a new type".  As an interesting digression, this is probably
the source of much confusion over casts, since many think that they are
conversion sometimes, and "look at T1-type bits through T2-colored
glasses" other times.  This is *not* the case... a cast is *always* a
conversion.  In C there are only two ways to take T1-typed-bits as if
they were T2-typed-bits.  Put the bits into a union of T1 and T2, or
cast a pointer to them.  (Well, they are the only two ways I could think
of, anyhow.)

Then note, in 5, objects and lvalues:

    The discussion of each operator below indicates whether it expects
    lvalue operands and whether it yields an lvalue.

Casts do not state that they yield lvalues, and postincrement expects
one.  So the combination is semantically illegal according to K&R.

In Harbison and Steele, things are simpler.  In chapter seven, they list
the operators that yield lvalues, which are

        e1[e2], (e) {if e is lvalue}, e.name {if e is lvalue}
        e->name, and *e

and conclude the paragraph, saying

    No other form of expression can produce an lvalue.

And in the same section, they state that postincrement requires an
lvalue.

The ANSI draft says in section 3.4

    Note: A cast does not return an lvalue.

As I say, Father, Son and Holy Ghost all agree on this point.

> Reasonable semantics
> could be defined for the operation, but I don't know how useful it is.

On first glance, it *does* seem reasonable.  But upon more detailed
thought, it turns out not to be the case.  The whole point is that
reasonable semantics *cannot* be defined for the operation in a machine
independant way.  The reasoning of the ANSI folks is spelled out in the
May '86 Dr. Dobb's Journal on page 20 in a section titled "Beating Dead
Horses" if you want to look it up, but the short form of this argument
is fairly simple.  You say that

        ((T *)P)++

can be given reasonable semantics.  This either means that pointers are
drastically different from other types (in ways that are inconvenient
and/or arbitrary), or this construction can also be given reasonable
semantics:

        ((T)L)++

Filling in something concrete for the type and the lvalue, and assuming
that "i" is an integer lvalue:

        ((float)i)++

Do you really want to contend that this is reasonable?  If so, what does
it mean?  Either the semantics of C must be made very irregular and
strange to allow ((T *)P)++, or the notion of any cast being an lvalue
must be discarded.  In addition to complexity and irregularity, possible
semantics of casts of lvalues *being* lvalues are extremely machine
dependant (in particular (in current implementations that allow them),
the pointer cases depend on the pointer format for all pointer types
being identical).  This is *not* something that is "reasonable" to add
to an allegedly machine independant language.
-- 
Wayne Throop      <the-known-world>!mcnc!rti-sel!dg_rtp!throopw



More information about the Comp.lang.c mailing list