Thanx.
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.
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.
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
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 ;-)
> 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.
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.
>> 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).
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"
How would that work in situations like this:
double d = 3.14;
int *p = & (int) d;
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.
Um, badly.
>> 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.
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.''
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.
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.
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.