casts and lvalues

R

Richard Heathfield

Keith Thompson said:
(The extension being discussed is allowing casts as lvalues.)

You're writing a tutorial about C, right?

I find my lack of faith disturbing.
 
R

Richard Heathfield

Keith Thompson said:
I didn't say that, but I agree, more or less.

Then I misrepresented you in a parallel reply.

I have to say I think you're wrong, but I'm open to persuasion.

The expression under consideration is toupper((unsigned char)c), and the
question is whether it constitutes an lvalue; your contention appears
to be that this (unsigned char)c argument is guaranteed by the Standard
to be an lvalue, because its value is assigned to an object during the
function call.

I think you're wrong for two reasons, which I invite you to knock down:

1) it might not even /be/ a function call;
2) lexically, we can think of a function call like this:

(a) the function call expression - toupper((unsigned char)c)
(b) the function declarator - int toupper(int ch)
(c) the function body

The business of copying argument-expression values to parameter objects
and JSRing to the function can be thought of as the transition between
(a) and (b). Whether you think an object is created for storing the
result of the expression in (a) or in (b) depends on where, precisely,
you draw the line between them. Syntactically, the object is created,
*at the very earliest*, "in" the function declarator, and there's a
case for saying it's not created until you actually get "into" (c), the
function body itself.

An *argument* is not an object.

Hear, hear. So we are indeed in agreement after all, it seems.
An argument is an expression
appearing between the parentheses in a function call;

or a macro.

But C99 6.9.1p9 says:

Each parameter has automatic storage duration. Its identifier is
an lvalue, which is in effect declared at the head of the compound
statement that constitutes the function body (and therefore cannot
be redeclared in the function body except in an enclosed block).

which implies that the parameter object isn't created until control
passes the opening '{' of the function.

Indeed.

But in any case, the question from upthread is whether this statement:

toupper((unsigned char)c);

contains an lvalue. c is an lvalue, but (unsigned char)c is not, and
statement does *not* contain an lvalue that refers to the parameter.

Right.
 
C

CBFalconer

Richard said:
CBFalconer said:

It could easily go into a register, but that's beside the point.
There's no object in the statement I presented.


Yes, but the statement I presented is not a function definition.
It is merely a function call.

Which, in turn, requires a location to hold a parameter. Hidden.
 
C

CBFalconer

Richard said:
.... snip ...

Two problems with this - firstly, you're trying to nudge the discussion
further and further into the called function in an attempt to shore up
your case, but we're discussing the cast-expression, not the execution
details of the called function; and secondly, there isn't necessarily a
function call at all! The expression, remember, was toupper((unsigned
char)c), and the implementation is perfectly at liberty to convert this
into something like:
(((unsigned char)c) == EOF) ? EOF : __toupper[((unsigned char)c)]

where __toupper is an array in read-only memory.

No it can't. That expression evaluates c twice, and is forbidden
for macro expansions of the std. library (apart from getc).
 
C

CBFalconer

Richard said:
Keith Thompson said:



Hear, hear. So we are indeed in agreement after all, it seems.

Nonsense. An argument is always an expression, converted into an
object, which is not accessible before entry to the sub-routine.
At that point it can be treated as any other local object.
 
J

jacob navia

CBFalconer said:
Nonsense. An argument is always an expression, converted into an
object, which is not accessible before entry to the sub-routine.
At that point it can be treated as any other local object.

That is exactly what I said.

jacob

P.S. But even if i would say
two plus two is four
I guess Mr heathfield would say

NO. It isn't.
 
R

Richard Heathfield

CBFalconer said:
Richard said:
... snip ...

Two problems with this - firstly, you're trying to nudge the
discussion further and further into the called function in an attempt
to shore up your case, but we're discussing the cast-expression, not
the execution details of the called function; and secondly, there
isn't necessarily a function call at all! The expression, remember,
was toupper((unsigned char)c), and the implementation is perfectly at
liberty to convert this into something like:
(((unsigned char)c) == EOF) ? EOF : __toupper[((unsigned char)c)]

where __toupper is an array in read-only memory.

No it can't. That expression evaluates c twice, and is forbidden
for macro expansions of the std. library (apart from getc).

Oops.

How about this:

((__zog = ((unsigned char)c)) == EOF) ? EOF :__toupper[__zog]
 
R

Richard Heathfield

jacob navia said:

P.S. But even if i would say
two plus two is four
I guess Mr heathfield would say

NO. It isn't.

Well, it needn't be; for example, in the ring containing only the
numbers 0, 1, 2, 3, 2 + 2 would be 0, not 4 - of course, that's merely
a nit-pick.

But in fact I'm perfectly happy to agree with you when you're right.
It's only when you're wrong that I disagree with you. That is why I
disagree with you so often.
 
M

mark

Oops.

How about this:

((__zog = ((unsigned char)c)) == EOF) ? EOF :__toupper[__zog]

But...

Weren't you trying to show that toupper((unsigned char)c) doesn't
necessarily contain an lvalue with the value of (unsigned char)c ?
 
R

Richard Heathfield

mark said:
Oops.

How about this:

((__zog = ((unsigned char)c)) == EOF) ? EOF :__toupper[__zog]

But...

Weren't you trying to show that toupper((unsigned char)c) doesn't
necessarily contain an lvalue with the value of (unsigned char)c ?

Touche'. (And a good spot. Clearly I lost concentration!)

Okay, this is the Last Chance Saloon, and relies on the implementation
defining EOF as -1 and __toupper having (at least) UCHAR_MAX + 2
entries:

__toupper[1 + ((unsigned char)c)]
 
E

Eric Sosman

jacob navia wrote On 06/26/07 03:28,:
The result of the expression is stored in the stack.
(Or its equivalent for the mythical machines without
stack)

Nonsense. On the machine in front of me at this
very moment, the result goes into a CPU register (if
an actual function call is made). The chance that
*anything* gets pushed onto a stack is roughly one
in eight.
 
E

Eric Sosman

Richard Heathfield wrote On 06/26/07 09:15,:
CBFalconer said:

Richard Heathfield wrote:

... snip ...
Two problems with this - firstly, you're trying to nudge the
discussion further and further into the called function in an attempt
to shore up your case, but we're discussing the cast-expression, not
the execution details of the called function; and secondly, there
isn't necessarily a function call at all! The expression, remember,
was toupper((unsigned char)c), and the implementation is perfectly at
liberty to convert this into something like:
(((unsigned char)c) == EOF) ? EOF : __toupper[((unsigned char)c)]

where __toupper is an array in read-only memory.

No it can't. That expression evaluates c twice, and is forbidden
for macro expansions of the std. library (apart from getc).


Oops.

How about this:

((__zog = ((unsigned char)c)) == EOF) ? EOF :__toupper[__zog]

Oops again, I think. What is the sign of __zog?

More usually, you get something euivalent to

__toupper[(c) - EOF]

.... which is another incentive for EOF to be minus one
rather than, say, INT_MIN.
 
J

jacob navia

Eric said:
jacob navia wrote On 06/26/07 03:28,:

Nonsense. On the machine in front of me at this
very moment, the result goes into a CPU register (if
an actual function call is made). The chance that
*anything* gets pushed onto a stack is roughly one
in eight.
If within the called function the address of that parameter is taken
it must be stored in the stack.
Obviously the compiler can optimize it away, etc.

But if it lives in a register, it still is somewhere there.

In the same vein, if you say:

int c;

c = 56;

The fact that the integer "c" is stored in a register doesn't mean that
it is not a lvalue isn't it?

We have to separate conceptually stores that can be optimized away, and
details of compiler decisions that store the integer in a register or
whatever.

If I declare
int f;
f = 56;

There is an "f" object even if the compiler realizes that f is not used
anywhere in the function and optimizes the whole stuff away!

jacob
 
E

Eric Sosman

jacob navia wrote On 06/26/07 11:10,:
If within the called function the address of that parameter is taken
it must be stored in the stack.

Nonsense again. The CPU registers on the machine
in front of me at this moment are not addressable as
if they were RAM, but some machines' registers are.
Obviously the compiler can optimize it away, etc.

But if it lives in a register, it still is somewhere there.

Your exact words were "The result of the expression is
stored in the stack." I do not believe that "somewhere
there" is an adequate defense of that statement.
 
F

Flash Gordon

Richard Heathfield wrote, On 26/06/07 09:34:
jacob navia said:
Thanks. You agree with me then, that the object exists, and exists
before the call, even if it is not accessible.

No, he doesn't, and neither do I. As he rightly said, the object doesn't
exist until the function is called (if indeed a function *is* called
for the expression under consideration, which is not necessarily the
case). It's a local object with automatic storage duration, so it
doesn't exist until the function call actually happens.
This is obvious if we read the standard 6.5.2.2 "Function calls",
where it is specified that after all assignments are done to the
function arguments there is a sequence point. At that sequence point
the arguments have been assigned but they are still not accessible
since the function call is not done yet.

Two problems with this - firstly, you're trying to nudge the discussion
further and further into the called function in an attempt to shore up
your case, but we're discussing the cast-expression, not the execution
details of the called function; and secondly, there isn't necessarily a
function call at all! The expression, remember, was toupper((unsigned
char)c), and the implementation is perfectly at liberty to convert this
into something like:
(((unsigned char)c) == EOF) ? EOF : __toupper[((unsigned char)c)]

No it isn't, since that evaluates c twice. It could, however, do:
__toupper[((unsigned char)c)+1]
and have EOF defined as -1.
where __toupper is an array in read-only memory.

*Now* where is your lvalue?

With my change to make it valid it is gone :)
Until the function (if there /is/ a function) returns, yes.

Indeed.
 
K

Keith Thompson

Richard Heathfield said:
Oops.

How about this:

((__zog = ((unsigned char)c)) == EOF) ? EOF :__toupper[__zog]

How about a few more parentheses?

I'm too lazy to figure out what just this does for
toupper(toupper(c)), but it's not pretty.
 
K

Keith Thompson

CBFalconer said:
Nonsense. An argument is always an expression, converted into an
object, which is not accessible before entry to the sub-routine.
At that point it can be treated as any other local object.

No, an argument is an expression whose value is *stored* in an object.
An argument and the corresponding parameter are two distinct things.
 
K

Keith Thompson

jacob navia said:
If within the called function the address of that parameter is taken
it must be stored in the stack.
Obviously the compiler can optimize it away, etc.

But if it lives in a register, it still is somewhere there.

In the same vein, if you say:

int c;

c = 56;

The fact that the integer "c" is stored in a register doesn't mean that
it is not a lvalue isn't it?

The object c is an object, not an lvalue.

The expression c on the left hand side of the assignment clearly is an
lvalue.
We have to separate conceptually stores that can be optimized away, and
details of compiler decisions that store the integer in a register or
whatever.

If I declare
int f;
f = 56;

There is an "f" object even if the compiler realizes that f is not used
anywhere in the function and optimizes the whole stuff away!

Of course f is an object.

Objects are not lvalues. Lvalues are not objects.
Arguments are not parameters. Parameters are not arguments.
Parameters are objects. Arguments are expressions, not objects.

What exactly are we arguing about here?

Let's take a simple example:

#include <stdio.h>
void func(int param)
{
printf("param = %d\n", param);
}
int main(void)
{
func(42); /* line 8 */
return 0;
}

In the following, I'll use quotation marks to delimit chunks of code.

The expression "42" on line 8 is an *argument* (not a parameter).
That expression is evaluated, and the result is assigned to "param",
which is a *parameter* (not an argument). The parameter "param",
which is an object, is created as part of the function call. The
result of evaluating the argument (an expression) is assigned to the
parameter (an object).

An argument and a parameter are two very different things. The
argument doesn't become, and is not converted to, the parameter; the
argument's value is assigned to the parameter.

I believe there's some ambiguity in the standard about just when the
parameter object is created. One passage implies it's created during
the execution of line 8, before "func" is actually called; another
implies that it's created during the execution of "func" itself, just
after execution passes the opening "{". But that ambiguity is not
really such a big deal; since nothing can access that object during
the window of ambiguity.

So the question we seem to be discussing is this: is there an lvalue
on line 8? The answer is clearly no.

What is an lvalue? Ignoring the way both the C90 and C99 standards
have screwed up the definition of the term, an lvalue is an expression
that designates an object. There are several expressions on line 8,
but none of them designate objects.

"42" clearly does not designate an object, so it's not an lvalue.
(The fact that the result of that subexpression is assigned to an
object doesn't make it an lvalue, any more than "43" in "x = 43;" is
an lvalue.)

"func" is a function designator, not an lvalue.

"func(42)" also does not designate an object; it merely yields a
value.

There is a separate question here: is an object created on line 8?
The answer is, I believe, ambiguous. If we assume that the parameter
object is created prior to the function call, then yes, an object is
created on line 8 (but, going back to the previous question, there is
no lvalue on line 8 that refers to that object).

Now if we want to discuss when parameters are created, that's great --
but that discussion doesn't need to consider lvalues.
 
C

CBFalconer

jacob said:
That is exactly what I said.

P.S. But even if i would say two plus two is four
I guess Mr heathfield would say "NO. It isn't."

The first sentence is fine. But why spend time sniping at poor
innocent innocuous inconspicuous Richard Heathfield, who has only
corrected errors?
 

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,997
Messages
2,570,239
Members
46,827
Latest member
DMUK_Beginner

Latest Threads

Top