Joe Wright said:
Keith Thompson wrote: [snip]
You could probably do it by considering the top-level operator of the
expression. For example, if the top-level operator is unary "sizeof",
unary "+", unary "-", multiplication, division, a bitwise or shift
operator, etc., then the expression cannot be an lvalue; if the
top-level operator is "[]", "->", etc., then it may be an lvalue. You
also have to allow for parentheses, and remember than x+y or x-y can
be an lvalue if x or y is a pointer.
Well no. x+y is always an rvalue. *(x+y) has a chance of being an lvalue.
D'oh, you're right.
In fact, *(x+y) is definitely an lvalue (if neither x nor y is a
pointer, then it's illegal).
With all respect to you and Eric and all the rest, I contend we make
too much of the 'complexity' of what an lvalue might be. I first
encountered the term in K&R1 circa 1978. That definition was clear to
me 20 years ago when I read it and satisfies me today. To wit: p183
A.5 "Objects and lvalues"
"An object is a manipulatable region of storage; an lvalue is an
expression referring to an object."
It worked for me then and it does now. KISS.
Agreed -- with the small proviso that an expression may be an lvalue
even if it doesn't *currently* refer to an object (but if it doesn't,
and it's used in a context requiring an lvalue, then the behavior is
undefined). For example, *ptr is an lvalue even if ptr == NULL.
Also an rvalue is any expression not an lvalue.
Um, not really. The standard doesn't use the term "rvalue" except in
passing in a footnote; the standard instead refers to the "value of an
expression", which is valid whether the expression happens to be an
lvalue or not. But that usage of "rvalue" is consistent with the
older meaning of "lvalue" (which someone, I don't remember who,
explained here recently). Namely, an lvalue and an rvalue are both
the result of evaluating an expression, but in different ways. An
rvalue is the result of evaluating an expression to determine an
ordinary value, such as 2+2 yielding 4. An lvalue, in this older
meaning, is the result of evaluating an expression to determine what
location it designates, such as *ptr yielding the memory location to
which ptr points (without regard to what may be currently stored in
that location). Subexpressions are evaluated for their rvalues or
their lvalues depending on the context in which they appear. If an
expression being evaluated for its lvalue doesn't currently designate
an object, the behavior is undefined.
The C standard dropped the term "rvalue" and changed the meaning of
"lvalue" so it refers to the expression itself, not to the result of
evaluating it in a particular way. In my opinion, the older usage was
more elegant; it may be a little harder to wrap you head around the
idea of evaluating something in two different ways, but it's a good
concept to have in your mental repertoire.
In my own view, even though an expression might refer to an object, it
becomes an lvalue only on the left of an assignment operator.
Consider:
int a, b, c; /* a, b and c are (refer to) objects */
a = 5; /* a is an lvalue */
b = a + 4; /* b is an lvalue, a is an rvalue and a + 4 is 9 */
That's a pretty good model too. It's a pity the standard isn't that
clear.