Google Groups no longer supports new Usenet posts or subscriptions. Historical content remains viewable.
Dismiss

Casts are not lvalues. Why?

23 views
Skip to first unread message

martin...@comcast.net

unread,
May 1, 2007, 9:35:06 AM5/1/07
to
Hi,
I understand in ANSI C, the cast of, say, void * to int * results
in a non-lvalue (6.5.4, footnote 85). However, I'm having trouble
finding the exact normative text which makes footnote 85 true.
Can anybody help me out?

Thanx.

Francis Glassborow

unread,
May 1, 2007, 10:13:51 AM5/1/07
to
I am having difficulty understanding how they could be lvalues. How
would you change the type of an object on the fly? Taking an int value
and generating the equivalent double value seems fine to me but taking
an int object (i.e. region of storage) and changing it into a double
object seems dangerous even if it the language provided some contortion
to allow code to be written that apparently did this.

It is effectively casting from one pointer type to another and we all
know how dangerous that is. C++ can support this by use of references
but there are safety mechanism provided (such as dynamic_cast<>) to
check if the operation is reasonable. However that is because C++ has
the concept of references and pointers having a static type and a
dynamic type.

Richard Tobin

unread,
May 1, 2007, 11:44:33 AM5/1/07
to
In article <1178026506....@e65g2000hsc.googlegroups.com>,
<martin...@comcast.net> wrote:

I only have the BSI version of C89 to hand, so I can't tell you the
section number, but in the definition of lvalue in the section
"Lvalues and function designators" it reads:

Except when it is the operand of the sizeof operator, the unary &
operator, the ++ operator, the -- operator, or the left operand of
the . operator or an assignment operator, an lvalue that does not
have array type is converted to the value stored in the designated
object (and is no longer an lvalue).

A pointer that is the operand of a cast operator does not fall under
the "except" clause there.

-- Richard

--
"Consideration shall be given to the need for as many as 32 characters
in some alphabets" - X3.4, 1963.

lawrenc...@ugs.com

unread,
May 1, 2007, 10:38:46 AM5/1/07
to
martin...@comcast.net wrote:
>
> I understand in ANSI C, the cast of, say, void * to int * results
> in a non-lvalue (6.5.4, footnote 85). However, I'm having trouble
> finding the exact normative text which makes footnote 85 true.

That's because it's a lack of text which makes it true -- nothing says
that result of a cast *is* an lvalue, so it's not. Since a cast
specifies a conversion, there is, in general, no object for it to refer
to. Even in the case you cite, there are systems where void * and int *
have different representations so the cast results in an actual change
of representation, just like casting double to int.

-Larry Jones

These findings suggest a logical course of action. -- Calvin

Eric Sosman

unread,
May 1, 2007, 12:22:35 PM5/1/07
to
martin...@comcast.net wrote On 05/01/07 09:35,:

The first sentence of 6.5.4p4 seems to cover it:

"Preceding an expression by a parenthesized type
name converts the value of the expression to the
named type."

So there are two values in play: That of the expression
that is the cast's operand, and the converted value produced
by the cast operator. The latter is derived from the former,
but once derived is independent of it: it's a value "in
flight," not a value that resides in an assignable object.

Trying to assign to the "in flight" value would seem
conceptually like trying to do `-x = 42' or `&y = NULL'
(maybe free() could make use of the latter ;-)

--
Eric....@sun.com

Richard Tobin

unread,
May 1, 2007, 12:28:40 PM5/1/07
to
In article <f17n91$2bc8$1...@pc-news.cogsci.ed.ac.uk>, I wrote:

> Except when it is the operand of the sizeof operator, the unary &
> operator, the ++ operator, the -- operator, or the left operand of
> the . operator or an assignment operator, an lvalue that does not
> have array type is converted to the value stored in the designated
> object (and is no longer an lvalue).
>
>A pointer that is the operand of a cast operator does not fall under
>the "except" clause there.

On further consideration I don't think this works. Given

(int *)p

it says that p is converted from an lvalue to a non-lvalue before the
cast operator is applied. It doesn't explain why the value of the
cast expression isn't an lvalue. If it refers to an object, as in

int i;
void *p = &i;
(int *)p

then it appears to fulfill the definition

An lvalue is an expression (with an object type or [...]) that
designates an object.

Wojtek Lerch

unread,
May 1, 2007, 12:43:03 PM5/1/07
to
"Richard Tobin" <ric...@cogsci.ed.ac.uk> wrote in message
news:f17pro$2c3a$1...@pc-news.cogsci.ed.ac.uk...

> it says that p is converted from an lvalue to a non-lvalue before the
> cast operator is applied. It doesn't explain why the value of the
> cast expression isn't an lvalue. If it refers to an object, as in
>
> int i;
> void *p = &i;
> (int *)p
>
> then it appears to fulfill the definition
>
> An lvalue is an expression (with an object type or [...]) that
> designates an object.

You seem to be mixing up the concepts of designating an object and pointing
to an object. The expression "p" is an lvalue that designates the object p.
The value of that object is a pointer that points to the object i. The
value of the expression "(int*)p" also points to the object i, but is not an
lvalue that designates the object p.


Richard Tobin

unread,
May 1, 2007, 12:51:14 PM5/1/07
to
In article <59p91nF...@mid.individual.net>,
Wojtek Lerch <Wojt...@yahoo.ca> wrote:

>> An lvalue is an expression (with an object type or [...]) that
>> designates an object.

>You seem to be mixing up the concepts of designating an object and pointing
>to an object. The expression "p" is an lvalue that designates the object p.
>The value of that object is a pointer that points to the object i. The
>value of the expression "(int*)p" also points to the object i, but is not an
>lvalue that designates the object p.

Oops yes, you're quite right, thanks.

The sentence I quoted appears to be defining lvalues in term of
designation. So the reason (int *)p is not an lvalue is that the
definition of the cast operator does not define its result to
designate anything (unlike, say, the definition of the indirection
operator).

Keith Thompson

unread,
May 1, 2007, 1:08:07 PM5/1/07
to

It would have been possible to make a cast an lvalue, and to define
the semantics. Presumably it would result in the reverse conversion.
For example:

void *vp;
int *ip = whatever;
(int*)vp = ip; /* not legal */

The assignment might be equivalent to:

vp = (void*)ip;

Similarly:

int i;
double d = 3.14;
(double)i = d; /* not legal */

would make the assignment equivalent to:

i = (int)d;

(Of course the cast is unnecessary in both cases, since the conversion
is implicit.)

Presumably the authors of the standard, and K&R before them, didn't
feel that this was worth the effort, a decision with which I agree
(for what little that's worth).

--
Keith Thompson (The_Other_Keith) ks...@mib.org <http://www.ghoti.net/~kst>
San Diego Supercomputer Center <*> <http://users.sdsc.edu/~kst>
"We must do something. This is something. Therefore, we must do this."
-- Antony Jay and Jonathan Lynn, "Yes Minister"

Wojtek Lerch

unread,
May 1, 2007, 1:32:14 PM5/1/07
to
"Keith Thompson" <ks...@mib.org> wrote in message
news:lnslag5...@nuthaus.mib.org...

> It would have been possible to make a cast an lvalue, and to define
> the semantics. Presumably it would result in the reverse conversion.
...

> int i;
> double d = 3.14;
> (double)i = d; /* not legal */
>
> would make the assignment equivalent to:
>
> i = (int)d;

How would that work in situations like this:

double d = 3.14;
int *p = & (int) d;


Eric Sosman

unread,
May 1, 2007, 1:49:35 PM5/1/07
to
Keith Thompson wrote On 05/01/07 13:08,:

>
> It would have been possible to make a cast an lvalue, and to define
> the semantics. Presumably it would result in the reverse conversion.
> For example:
>
> void *vp;
> int *ip = whatever;
> (int*)vp = ip; /* not legal */
>
> The assignment might be equivalent to:
>
> vp = (void*)ip;
> [...]

Some pre-Standard implementations had semantics that
were somewhat like this. You found code like

unsigned char buff[BUFSIZE];
unsigned char *ptr = buff;
...
/* store one byte in buffer */
*ptr++ = '?';
...
/* store four-byte integer in buffer */
*((int*)ptr)++ = integer;
...
/* store eight-byte double in buffer */
*((double*)ptr)++ = sin(x);
...

... where the lvalue-ness of the cast expression (pre-
Standard, remember) made possible the ++ operators.

This tended not to work very well on systems where
different types of pointers had different representations.

--
Eric....@sun.com

Keith Thompson

unread,
May 1, 2007, 4:18:16 PM5/1/07
to

Um, badly.

Richard Tobin

unread,
May 1, 2007, 5:25:08 PM5/1/07
to
In article <1178041776.121620@news1nwk>,
Eric Sosman <Eric....@Sun.COM> wrote:

>> void *vp;
>> int *ip = whatever;
>> (int*)vp = ip; /* not legal */
>>
>> The assignment might be equivalent to:
>>
>> vp = (void*)ip;

> Some pre-Standard implementations had semantics that


>were somewhat like this. You found code like
>
> unsigned char buff[BUFSIZE];
> unsigned char *ptr = buff;
> ...
> /* store one byte in buffer */
> *ptr++ = '?';
> ...
> /* store four-byte integer in buffer */
> *((int*)ptr)++ = integer;
> ...
> /* store eight-byte double in buffer */
> *((double*)ptr)++ = sin(x);
> ...

This is more straightforward than the general case of using a cast on
the LHS. It doesn't really assume that pointers all have the same
representation, just that the uncast pointer type is the
finest-grained.

Of course, on systems where all pointers have the same representation,
the cast is a no-op as far as the underlying bits are concerned, so
it's particularly simple. On those systems it is also possible to use
a union of types:

union {char *c; int *i; double *d;} ptr;
...
*(ptr.c++) = '?';
*(ptr.i++) = 42;

A common use for this is (or was) in virtual machine interpreters,
where the pointer is a "program counter" that can address
variously-sized opcodes and operands.

Of course one could do this more portably as

char *ptr;

*(int *)ptr = 42; ptr += sizeof(int);

but in the old days this typically produced worse code, which was an
important consideration when implementing a virtual machine
interpreter.

Jun Woong

unread,
May 2, 2007, 4:36:56 AM5/2/07
to
Keith Thompson <k...@mib.org> wrote:
[...]

>
> It would have been possible to make a cast an lvalue, and to define
> the semantics. Presumably it would result in the reverse conversion.
> For example:
>
> void *vp;
> int *ip = whatever;
> (int*)vp = ip; /* not legal */
>
> The assignment might be equivalent to:
>
> vp = (void*)ip;
>

That doesn't looks intuitive to me. I would expect

(T)object = value;

works as

*(T *)&object = value;


--
Jun, Woong (woong at icu.ac.kr)
Samsung Electronics Co., Ltd.

``All opinions expressed are mine, and do not represent
the official opinions of any organization.''

Keith Thompson

unread,
May 2, 2007, 5:22:53 AM5/2/07
to
Jun Woong <wo...@icu.ac.kr> writes:
> Keith Thompson <k...@mib.org> wrote:
> [...]
>>
>> It would have been possible to make a cast an lvalue, and to define
>> the semantics. Presumably it would result in the reverse conversion.
>> For example:
>>
>> void *vp;
>> int *ip = whatever;
>> (int*)vp = ip; /* not legal */
>>
>> The assignment might be equivalent to:
>>
>> vp = (void*)ip;
>>
>
> That doesn't looks intuitive to me. I would expect
>
> (T)object = value;
>
> works as
>
> *(T *)&object = value;

But we already have "*(T *)&object = value;".

Given your suggested semantics, it would interpret the representation
of "object" as if it were of type T; it wouldn't be a conversion
involving the actual value of "object".

(My thought was inspired by Ada, which does this kind of reverse
conversion in some cases involving parameter passing.)

But Wojtek Lerch brought up an example where my hypothetical semantics
would break down. It wasn't a proposal in the first place; it turns
out that it's not even a very good hypothetical.

Jun Woong

unread,
May 2, 2007, 6:04:28 AM5/2/07
to
Keith Thompson <k...@mib.org> wrote:

> Jun Woong <w...@icu.ac.kr> writes:
> > Keith Thompson <k...@mib.org> wrote:
> > [...]
>
> >> It would have been possible to make a cast an lvalue, and to define
> >> the semantics. Presumably it would result in the reverse conversion.
> >> For example:
>
> >> void *vp;
> >> int *ip = whatever;
> >> (int*)vp = ip; /* not legal */
>
> >> The assignment might be equivalent to:
>
> >> vp = (void*)ip;
>
> > That doesn't looks intuitive to me. I would expect
>
> > (T)object = value;
>
> > works as
>
> > *(T *)&object = value;
>
> But we already have "*(T *)&object = value;".
>

Simiraly, we already have

vp = (void *)ip;

for

(int *)vp = ip; /* imaginary */

Though, as you did, I didn't mean a serious proposal anyway.

> Given your suggested semantics, it would interpret the representation
> of "object" as if it were of type T; it wouldn't be a conversion
> involving the actual value of "object".
>

I tried to separate the role of the cast in the "lvalue" context
from that in the "rvalue" context. It does a conversion when used for
the "rvalue" context, but works as a syntactic sugar when used for
the "lvalue" context. This is from my way to read the cast in codes;
when I see a cast (T), I read it as "read the value as having type
T" just for convenience.

Nick Maclaren

unread,
May 3, 2007, 6:48:31 AM5/3/07
to

In article <lnslag5...@nuthaus.mib.org>,

Keith Thompson <ks...@mib.org> writes:
|>
|> It would have been possible to make a cast an lvalue, and to define
|> the semantics. Presumably it would result in the reverse conversion.

Just like the infinite syntactic loop that is permitted with pointers
and arrays :-( C++ fixes that one.

|> Presumably the authors of the standard, and K&R before them, didn't
|> feel that this was worth the effort, a decision with which I agree
|> (for what little that's worth).

It wouldn't have fallen naturally out of the BCPL model, which was
used as the basis for C's semantic model.


Regards,
Nick Maclaren.

0 new messages