casts and lvalues

J

jacob navia

Continuing the discussion about casts, I would like to know
your opinions about the hairy subject of casts as lvalues, i.e.
int main(void)
{
long long a;
char *n;

(char *)a = n;
}

This will fail under lcc-win32, but MSVC and gcc will
accept it. I know that the standard prescribes the behavior
that lcc-win32 uses, but I left that behavior after a big
discussion about this several years ago. I had modified it,
and some people raised hell.

What are the problems of doing this? I mean not the usual
"the standard says so" but what problems would arise within the
language if this would be accepted?

Apparently gcc and msvc are still used, and this doesn't seem
to produce any big problems.

Thanks in advance for your comments, and I thank all people
that participated in the discussion yesterday.

jacob
 
H

Harald van =?UTF-8?B?RMSzaw==?=

jacob said:
Continuing the discussion about casts, I would like to know
your opinions about the hairy subject of casts as lvalues, i.e.
[...]
What are the problems of doing this? I mean not the usual
"the standard says so" but what problems would arise within the
language if this would be accepted?

int a = 1;
void f(double *d) {
if (a == 1) *d = 2;
if (a == 2) *d = 0;
}
int main(void) {
f(&(double) a);
return a;
}

What would this program return?
 
R

Richard Heathfield

jacob navia said:
Continuing the discussion about casts, I would like to know
your opinions about the hairy subject of casts as lvalues

It isn't a matter of opinion. They're not legal C. A cast yields a
value, but not an lvalue.

What are the problems of doing this?

It doesn't compile.
I mean not the usual
"the standard says so" but what problems would arise within the
language if this would be accepted?

Implementors jumped at C89 like a pack of starving stoats on a squirrel.
But they have stayed away from C99 in droves. I don't see them suddenly
all agreeing to support a particular extension, all with the same
syntax and semantics. So the fundamental problem would be that your
proposal wouldn't be accepted.
 
J

Joe Wright

Harald said:
jacob said:
Continuing the discussion about casts, I would like to know
your opinions about the hairy subject of casts as lvalues, i.e.
[...]
What are the problems of doing this? I mean not the usual
"the standard says so" but what problems would arise within the
language if this would be accepted?

int a = 1;
void f(double *d) {
if (a == 1) *d = 2;
if (a == 2) *d = 0;
}
int main(void) {
f(&(double) a);
return a;
}

What would this program return?

It doesn't compile here. You seem to want to pretend a is a double.

f((double*)&a);

will at least compile.
 
J

jacob navia

Richard said:
jacob navia said:


It isn't a matter of opinion. They're not legal C. A cast yields a
value, but not an lvalue.



It doesn't compile.


Implementors jumped at C89 like a pack of starving stoats on a squirrel.
But they have stayed away from C99 in droves. I don't see them suddenly
all agreeing to support a particular extension, all with the same
syntax and semantics. So the fundamental problem would be that your
proposal wouldn't be accepted.

But this is just politics. I do not want to do any proposal nor do
I want to modify anything. I just want to know what TECHNICAL problems
could arise if that extension (that is provided at least by the
compilers I mentioned) would be part of the language.

Why are casts not lvalues? What TECHNICAL reasons exist for that?
That is my question.
 
R

Richard Tobin

This will fail under lcc-win32, but MSVC and gcc will
accept it.

Gcc has, I think, always warned about this if you give enough flags,
and seems to reject it by default in recent versions.
What are the problems of doing this? I mean not the usual
"the standard says so" but what problems would arise within the
language if this would be accepted?

So long as you can specifiy exactly what it means, there would be no
problem adding it to the language. Of course, once you've specified
exactly what it means it's easy enough to manage without it.

I've always thought its use in implementing a "program counter"
variable was reasonable, where a generic pointer is cast to various
types and incremented by the same amount, e.g.

op = *((operator *)pc)++;
switch(op)
{
...
case ADD:
arg1 = *((int *)pc)++;
arg2 = *((int *)pc)++;
whatever = arg1 + arg2;
...

But those writing this style of code can easily enough work around it.
Apparently gcc and msvc are still used, and this doesn't seem
to produce any big problems.

Apparently gcc's implementors see sufficient problems to not accept it
by default, but those problems may just be compatibility ones.

-- Richard
 
M

Mark McIntyre

On Sun, 24 Jun 2007 23:20:03 +0200, in comp.lang.c , jacob navia

(of
long long a;
char *n;

(char *)a = n;
)
Why are casts not lvalues? What TECHNICAL reasons exist for that?
That is my question.

Because its meaningless in my view. What exactly does the above do?

--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 
J

jacob navia

Mark said:
On Sun, 24 Jun 2007 23:20:03 +0200, in comp.lang.c , jacob navia

(of
long long a;
char *n;

(char *)a = n;
)


Because its meaningless in my view. What exactly does the above do?
Moves the contents of n (an address) into the memory locations of a,
that can hold a pointer is sizeof(void *) <= sizeof(long long);

I am not saying that this is nice/the best thing since sliced bread
but that is what it does.
 
K

Keith Thompson

jacob navia said:
Continuing the discussion about casts, I would like to know
your opinions about the hairy subject of casts as lvalues, i.e.


This will fail under lcc-win32, but MSVC and gcc will
accept it. I know that the standard prescribes the behavior
that lcc-win32 uses, but I left that behavior after a big
discussion about this several years ago. I had modified it,
and some people raised hell.

What are the problems of doing this? I mean not the usual
"the standard says so" but what problems would arise within the
language if this would be accepted?
[...]

This extension doesn't seem to me to be particularly useful. The
above would be more clearly written as:

long long a;
char *n;
a = (long long)n;

Another problem with this, as with any extension, is that it
encourages programmers to write non-portable code that depends on it.
 
R

Richard Tobin

Mark McIntyre said:
long long a;
char *n;

(char *)a = n;
Because its meaningless in my view.

C constructs are no natural phenomena. They are meaningful if people
assign meaning to them. And several compilers *have* assigned meaning
to casts as lvalues,
What exactly does the above do?

I believe that it's intended to be equivalent to "a = (long long)n'.
As such, it's not very useful. Some more complicated uses are more
obviously useful, as I've noted elsewhere.

-- Richard
 
J

jacob navia

Keith said:
jacob navia said:
Continuing the discussion about casts, I would like to know
your opinions about the hairy subject of casts as lvalues, i.e.

This will fail under lcc-win32, but MSVC and gcc will
accept it. I know that the standard prescribes the behavior
that lcc-win32 uses, but I left that behavior after a big
discussion about this several years ago. I had modified it,
and some people raised hell.

What are the problems of doing this? I mean not the usual
"the standard says so" but what problems would arise within the
language if this would be accepted?
[...]

This extension doesn't seem to me to be particularly useful. The
above would be more clearly written as:

long long a;
char *n;
a = (long long)n;

Granted.

It is clearer but the question is not if this is pleasing but if there
would be any problems (in the sense of contradictions or buggy language
specifications) if casts could be lvalues.
Another problem with this, as with any extension, is that it
encourages programmers to write non-portable code that depends on it.

Yes, of course. lcc-win32 doesn't even support this extension. But
if I would add support for it, are there any TECHNICAL drawbacks?
I mean, if somebody asks me to support overloading addition
with

int operator+(struct FOO *a,int b);

I would say NO that can't be done because addition of a pointer and
an integer is already used in the language to access the nth element
using a pointer as base, i.e.
struct FOO *p;
p + 6
addresses the sixth element after the element pointed by p. If I would
implement such an extension I would introduce an ambiguity in the
language.

Is that the case with casts as lvalues?
 
C

Chris Torek


Gcc has, I think, always warned about this if you give enough flags,
Indeed.

and seems to reject it by default in recent versions.

Yes. This illustrates one of the dangers of relying on compiler
extensions: the compiler-writer may eventually realize the folly
of that particular extension, and remove it. (The extension, not
the folly. :) )

GCC's old definition of (cast)obj = expr was:

((cast)(obj = (T)(expr)))

where T is the type of the object "obj", -- so the code above, with
"a" being "long long", really "meant":

(char *)(a = (long long)n);

Many programmers seemed to have expected it to mean:

*(char *)&a = n;

which, on a typical 32-bit-pointer, 64-bit-"long long" machine,
sets just half of the object "a", leaving the other half unmodified.
GCC's old interpretation sets all 64 bits. The overly-clever
programmer, believing it set only 32 bits, set the other 32 bits
to something useful first, and then was surprised when those bits
were clobbered.

(If you use Standard C to write "what you mean", you will not be
surprised by whatever bizarre interpretation some compiler-writer
uses for his or her particular extension.)
I've always thought its use in implementing a "program counter"
variable was reasonable, where a generic pointer is cast to various
types and incremented by the same amount, e.g.

op = *((operator *)pc)++;
switch(op)
{
...
case ADD:
arg1 = *((int *)pc)++;
arg2 = *((int *)pc)++;
whatever = arg1 + arg2;
...

But those writing this style of code can easily enough work around it.

Yes -- and, assuming "void *pc" for the above and that "operator" is
a typedef-name for a type smaller than plain "int", the above tends
to malfunction on various machines. So it might not be the best
way to write the code, even with the more verbose workaround:

op = *(operator *)pc, pc = (unsigned char *)pc + sizeof(operator);
switch (op)
{
...
case ADD:
arg1 = *(int *)pc, pc = (unsigned char *)pc + sizeof(int);
arg2 = *(int *)pc, pc = (unsigned char *)pc + sizeof(int);
whatever = arg1 + arg2;

where the comma-expression versions above might be hidden behind
a macro and then modified a bit:

#define FETCH(ty, pc) \
(pc = (unsigned char *)(pc) + sizeof(ty), ((ty *)pc)[-1])

Note: giving "pc" type "unsigned char *" simplifies this even
further:

#define FETCH(ty, pc) (((ty *)(pc += sizeof(ty)))[-1])

switch (FETCH(operator, pc)) {
...
case ADD:
arg1 = FETCH(int, pc);
whatever = arg1 + FETCH(int, pc);
...

The alignment trap remains, however: for this to work in general,
the pointer must remain aligned for each type "ty". The simplest
way to guarantee such alignment is to use just a single type, in
this case, "int". If "pc" is always going to refer to "int"s, we
can remove the FETCH macro's "ty" argument, change "pc" to "int
*", and simply use *pc++ -- and thus remove all need for the FETCH
macro itself.
 
K

Keith Thompson

jacob navia said:
But this is just politics. I do not want to do any proposal nor do
I want to modify anything. I just want to know what TECHNICAL problems
could arise if that extension (that is provided at least by the
compilers I mentioned) would be part of the language.

Why are casts not lvalues? What TECHNICAL reasons exist for that?
That is my question.

Why *should* casts be lvalues?

For any proposed addition to the language, the burden is on its
advocates to demonstrate that its advantages outweigh the costs of
updating the standard *and* of updating every implementation to
support it.

The main technical problem, in my opinion, is the difficulty of
defining the semantics in all possible cases, something that must be
done if this is to become a language feature. I see no particular
advantage that outweighs this difficulty; I know of no use of
cast-as-lvalue that can't be written equivalently in legal C.

A cast specifies a conversion of a given value (the result of
evaluating an expression) to a specified type. That's relatively
simple to describe and to understand. Allowing casts as lvalues would
require a much more complex model of what casts mean.

I presume you're proposing that a cast would be an lvalue only if the
operand is itself an lvalue. That's just one of the many details that
must be specified before any such proposal can be taken seriously.

Even if such a feature were added to a future version of the standard,
there's a real risk that some obscure corner case could be neglected,
leading to a flaw in the standard. In two attempts a decade apart,
the commitee hasn't even managed to come up with a coherent definition
of the word "lvalue".
 
O

Old Wolf

Harald said:
jacob said:
Continuing the discussion about casts, I would like to know
your opinions about the hairy subject of casts as lvalues, i.e.
[...]
What are the problems of doing this? I mean not the usual
"the standard says so" but what problems would arise within the
language if this would be accepted?
int a = 1;
void f(double *d) {
if (a == 1) *d = 2;
if (a == 2) *d = 0;
}
int main(void) {
f(&(double) a);
return a;
}
What would this program return?

It doesn't compile here. You seem to want to pretend a is a double.

Did you read the text you quoted? The point of
this thread is to ask what problems would occur
if the above code were made legal.
 
P

pete

jacob said:
Continuing the discussion about casts, I would like to know
your opinions about the hairy subject of casts as lvalues, i.e.

I don't like it because the semantics are srewed up.
An lvalue converted to a different type,
refers to memory that was never reserved.
 
C

CBFalconer

jacob said:
Continuing the discussion about casts, I would like to know
your opinions about the hairy subject of casts as lvalues, i.e.


This will fail under lcc-win32, but MSVC and gcc will
accept it. I know that the standard prescribes the behavior
that lcc-win32 uses, but I left that behavior after a big
discussion about this several years ago. I had modified it,
and some people raised hell.

Don't know about MSVC, but gcc (later versions) will also reject
this. A cast cannot be a lvalue. Also you have the types
confused.
 
C

CBFalconer

jacob said:
.... snip ...

Why are casts not lvalues? What TECHNICAL reasons exist for that?
That is my question.

A cast requires a destination (i.e. a lvalue) to receive the
converted object. Without a destination, such does not exist.
 
R

Richard Heathfield

CBFalconer said:
A cast requires a destination (i.e. a lvalue) to receive the
converted object.

No, it doesn't. No object is converted by a cast, and casts do not
*require* lvalues to receive their results. For example, there is no
lvalue in the (pointless but legal) statement:

toupper((unsigned char)c);
 
J

Jack Klein

Continuing the discussion about casts, I would like to know
your opinions about the hairy subject of casts as lvalues, i.e.


This will fail under lcc-win32, but MSVC and gcc will
accept it. I know that the standard prescribes the behavior
that lcc-win32 uses, but I left that behavior after a big
discussion about this several years ago. I had modified it,
and some people raised hell.

I just tried this with Visual Studio 2005 Express, and indeed you have
to set the warning level to maximum (/W4) to even get it to issue a
misleadingly incorrectly worded error massage:

"warning C4213: nonstandard extension used : cast on l-value"

....but it still produces a 32-bit executable. When I initialize a to
a value and n to NULL, it sets the low 32 bits of a to its
representation of a NULL pointer (0), and leaves the high 32 bits
unchanged.

The only version of gcc that I have installed at the moment is 3.4.2,
(mingw), and it gives me these two diagnostics:

"warning: cast to pointer from integer of different size"
"warning: use of cast expressions as lvalues is deprecated"
What are the problems of doing this? I mean not the usual
"the standard says so" but what problems would arise within the
language if this would be accepted?

What do you do with:

(char *)(3LL + 5LL) = n;

....???
Apparently gcc and msvc are still used, and this doesn't seem
to produce any big problems.

Well, actually it does produce the big problem of continuing to allow
programmers to write incorrect and quite possibly unsafe code. What
happens when they cast a value with a narrower representation to one
with a wider representation?
Thanks in advance for your comments, and I thank all people
that participated in the discussion yesterday.

Personally, I'm against this. If the cast is to a type with more bits
than the original, you risk overwriting memory incorrectly, with all
of the defect and security issues that involves. If the cast is to a
type with more bits than the original, you risk creating an invalid
trap value in the object.

And, of course, if somebody really needs to do this, or at least
thinks that they do, it is perfectly legal to write:

*(char *)(&a) = n;

This is hideously ugly and stands out like a sore thumb, as well it
should.

If you start down this path, you open a can of worms that means you
need to scrutinize all operators that currently yield (r)values rather
than lvalues. Where and why do you stop?

Given:

void func(int *ip);

What should happen with:

int x = 3;
func(&(x + 2));

You could indeed do this, at the expense of turning C into BASIC (or
other languages), that will construct a temporary object for you.

I just don't think the gain is worth the change, especially since
there is a valid way to write a statement that does the same thing.

--
Jack Klein
Home: http://JK-Technology.Com
FAQs for
comp.lang.c http://c-faq.com/
comp.lang.c++ http://www.parashift.com/c++-faq-lite/
alt.comp.lang.learn.c-c++
http://www.club.cc.cmu.edu/~ajo/docs/FAQ-acllc.html
 
M

Mark McIntyre

Moves the contents of n (an address) into the memory locations of a,

with you so far...
that can hold a pointer is sizeof(void *) <= sizeof(long long);

.... but not here.
The value of n is assigned to a. Ok. If n is larger than *a, there's a
memory overwrite. How does this 'cast' help with this???
I am not saying that this is nice/the best thing since sliced bread
but that is what it does.

I probably misunderstand, because to me the above is gibberish.
--
Mark McIntyre

"Debugging is twice as hard as writing the code in the first place.
Therefore, if you write the code as cleverly as possible, you are,
by definition, not smart enough to debug it."
--Brian Kernighan
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Similar Threads

Casts on lvalues 74
Casts 81
Union and pointer casts? 13
casts and pointers 0
Pointer casts for OOP 2
sub-int types, casts, MISRA and RH's writings 6
Question about casts 7
Pointer to Structure Casts 5

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,014
Latest member
BiancaFix3

Latest Threads

Top