r- r-value vs l-value?

Z

Zach

Can someone please explain what this means and illustrate the
difference with some code.

Thanks,
Zach
 
C

Chris Smith

Zach said:
Can someone please explain what this means and illustrate the
difference with some code.

Sure. An l-value is an expression that may legally appear on the left-
hand side of the assignment operator, =. An r-value is an expression
that may appear on the right-hand side. The set of r-values is a subset
of the the set of l-values. For example, given:

int a;
int b[50];

The following are l-values (and also r-values):

a
b[10]

Whereas the following are ONLY r-values:

17
a + 5
b

Therefore, the following line of code will fail to compile:

a + 5 = 15;

Hope that helps,
 
R

Richard Heathfield

Chris Smith said:
Sure. An l-value is an expression that may legally appear on the left-
hand side of the assignment operator, =.

More formally, "an lvalue is an expression with an object type or an
incomplete type other than void; if an lvalue does not designate an object
when it is evaluated, the behaviour is undefined."

An r-value is an expression
that may appear on the right-hand side. The set of r-values is a subset
of the the set of l-values. For example, given:

int a;
int b[50];

The following are l-values (and also r-values):

a
b[10]

Whereas the following are ONLY r-values:

17
a + 5
b

Strictly speaking, b is an lvalue, but not a modifiable lvalue. Section
6.3.2.1(1) - which I quoted above - goes on to say: "A modifiable lvalue is
an lvalue that does not have array type, does not have an incomplete type,
does not have a const-qualified type, and if it is a structure or union,
does not have any member (including, recursively, any member or element of
all contained aggregates or unions) with a const-qualified type."

<snip>
 
C

Chris Smith

Richard Heathfield said:
Strictly speaking, b is an lvalue, but not a modifiable lvalue. Section
6.3.2.1(1) - which I quoted above - goes on to say: "A modifiable lvalue is
an lvalue that does not have array type, does not have an incomplete type,
does not have a const-qualified type, and if it is a structure or union,
does not have any member (including, recursively, any member or element of
all contained aggregates or unions) with a const-qualified type."

Interesting... is there a situation in legal code where this makes a
difference?
 
Z

Zach

Chris said:
Sure. An l-value is an expression that may legally appear on the left-
hand side of the assignment operator, =. An r-value is an expression
that may appear on the right-hand side. The set of r-values is a subset
of the the set of l-values. For example, given:

int a;
int b[50];

The following are l-values (and also r-values):

a
b[10]

Whereas the following are ONLY r-values:

17
a + 5
b

Therefore, the following line of code will fail to compile:

a + 5 = 15;

Hope that helps,

Where are the rules of what may go on each side?

Zach
 
K

Keith Thompson

Richard Heathfield said:
Chris Smith said:

More formally, "an lvalue is an expression with an object type or an
incomplete type other than void; if an lvalue does not designate an object
when it is evaluated, the behaviour is undefined."
[...]

Which, as I've argued before, is a poor definition.

The C90 standard's definition is:

An _lvalue_ is an expression (with an object type or an incomplete
type other than void) that designates an object.

The problem with this is that, if it's interpreted literally, certain
expressions are or are not lvalues depending on their current values.
For exmaple if p is a pointer to int that currently points to an int
object, then *p is clearly an lvalue -- but if p == NULL, then *p
doesn't currently designate an object. Since lvalueness determines
compile-time legality, this clearly wasn't the intent.

The C99 standard's definition, which Richard quoted above, was
intended to correct this problem -- and it did, but at the expense of
introducing another one. Designating an object is what lvalueness is
all about; the C99 definition lost this. For example, 42 is "an
expression with an object type ...", so, if the definition is to be
taken literally, 42 is an lvalue. And since it doesn't designate an
object, evaluating 42 invokes undefined behavior. Again, this clearly
isn't what was intended.

What the definition *should* say is that an lvalue is an expression
with an object type or an incomplete type other than void that
designates or *potentially* designates an object. For example, *p
potentially designates an object, depending on the current value of p,
but *p is an lvalue regardless of the value of p. C99's statement
that "if an lvalue does not designate an object when it is evaluated,
the behaviour is undefined" is then valid. The problem is defining
the phrase "potentially designates" (or some equivalent phrase) in
standardese.
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

Chris said:
Sure. An l-value is an expression that may legally appear on the left-
hand side of the assignment operator, =. An r-value is an expression
that may appear on the right-hand side. The set of r-values is a subset
of the the set of l-values. For example, given:

int a;
int b[50];

The following are l-values (and also r-values):

a
b[10]

Whereas the following are ONLY r-values:

17
a + 5
b

Therefore, the following line of code will fail to compile:

a + 5 = 15;

Hope that helps,

Where are the rules of what may go on each side?

A nice rule is "when it makes sense". An l-value is something you can
(after evaluation) assign a value to and an r-value is a value (again,
after evaluation) that can be assigned. Generally speaking this means
that an l-value evaluates to a variable (or and index into an array)
while an r-value evaluates to a value of the same type as the l-value.

Erik Wikström
 
O

onkar

L-value -> something u can store to -> mem location
R-value -> something u can read from -> variable
you cant store to (a+5) , if a is int
but you can store to *(a+5), where a is int*

Onkar

Chris said:
Sure. An l-value is an expression that may legally appear on the left-
hand side of the assignment operator, =. An r-value is an expression
that may appear on the right-hand side. The set of r-values is a subset
of the the set of l-values. For example, given:

int a;
int b[50];

The following are l-values (and also r-values):

a
b[10]

Whereas the following are ONLY r-values:

17
a + 5
b

Therefore, the following line of code will fail to compile:

a + 5 = 15;

Hope that helps,

Where are the rules of what may go on each side?

A nice rule is "when it makes sense". An l-value is something you can
(after evaluation) assign a value to and an r-value is a value (again,
after evaluation) that can be assigned. Generally speaking this means
that an l-value evaluates to a variable (or and index into an array)
while an r-value evaluates to a value of the same type as the l-value.

Erik Wikström
 
?

=?ISO-8859-1?Q?Erik_Wikstr=F6m?=

L-value -> something u can store to -> mem location
R-value -> something u can read from -> variable

I'd say R-value -> something with a value -> variable, constant, etc.

Erik Wikström
 
W

whyglinux

Erik said:
I'd say R-value -> something with a value -> variable, constant, etc.

Variables are typically lvalues, not rvalues though lvalues can be
converted to rvalues as needed. Constants, indeed, are rvalues and can
never be lvalues in C.
 
J

John Bode

Zach said:
Can someone please explain what this means and illustrate the
difference with some code.

Thanks,
Zach

An lvalue is an expression that refers to an object (a region of
memory) in such a way that the object may be read or modified. An
rvalue is an expression that is not an lvalue.

Only lvalues may appear on the left-hand side of an assignment
expression, but not all lvalues may be writable.
From Harbison & Steele, 5th ed., the following expressions may be
lvalues, provided they do not have the type "array of ..." :

e // e must be a variable name
e[k]
(e) // e must be an lvalue
e.name // e must be an lvalue
e->name
*e
string-constant

The following expressions may *not* be lvalues:

array names
functions
enumeration constants
assignment expressions
casts
function calls
 
K

Keith Thompson

Erik Wikström said:
I'd say R-value -> something with a value -> variable, constant, etc.

The C standard doesn't use the term "rvalue" except in one footnote:

The name "lvalue" comes originally from the assignment expression
E1 = E2, in which the left operand E1 is required to be a
(modifiable) lvalue. It is perhaps better considered as
representing an object "locator value". What is sometimes called
"rvalue" is in this International Standard described as the "value
of an expression".

An lvalue is an expression that designates an object, or that can
designate an object. (The latter is necessary to cover cases like
"*ptr", where ptr is a pointer to some object type; it may or may not
actually designate an object depending on its current value, but it's
an lvalue regardless.) (The C90 standard's definition of lvalue was
flawed; the C99 standard's correction just made it worse.)

An rvalue, if you insist on using the term, is simply the value of an
expression.
 
C

Chris Smith

John Bode said:
From Harbison & Steele, 5th ed., the following expressions may be
lvalues, provided they do not have the type "array of ..." :

e // e must be a variable name
e[k]
(e) // e must be an lvalue
e.name // e must be an lvalue
e->name
*e
string-constant

The following expressions may *not* be lvalues:

array names
functions
enumeration constants
assignment expressions
casts
function calls

Array names? Isn't that the same mistake I made a bit earlier?
 
M

muttaa

Chris said:
John Bode said:
From Harbison & Steele, 5th ed., the following expressions may be
lvalues, provided they do not have the type "array of ..." :

e // e must be a variable name
e[k]
(e) // e must be an lvalue
e.name // e must be an lvalue
e->name
*e
string-constant

The following expressions may *not* be lvalues:

array names
functions
enumeration constants
assignment expressions
casts
function calls

Array names? Isn't that the same mistake I made a bit earlier?

Mr.Chris, will you please explain how array names can be an l-value?
 
K

Keith Thompson

John Bode said:
An lvalue is an expression that refers to an object (a region of
memory) in such a way that the object may be read or modified. An
rvalue is an expression that is not an lvalue.

The origin of the terms is that an "lvalue" can appear on the left
hand side of an assignment, and an "rvalue" can appear on the right
hand side of an assignment. Given this:

int x, y;
x = y;

y is clearly an lvalue (since it refers to an object), but it's also
an rvalue (it appears on the RHS of the assignment).

This kind of confusion is probably why the standard doesn't use the
term "rvalue" (except in passing in a single footnote).
 
J

John Bode

Chris said:
John Bode said:
From Harbison & Steele, 5th ed., the following expressions may be
lvalues, provided they do not have the type "array of ..." :

e // e must be a variable name
e[k]
(e) // e must be an lvalue
e.name // e must be an lvalue
e->name
*e
string-constant

The following expressions may *not* be lvalues:

array names
functions
enumeration constants
assignment expressions
casts
function calls

Array names? Isn't that the same mistake I made a bit earlier?

H&S appear to be a little inconsistent on this point. In one paragraph
they seem to consider arrays to be non-modifiable lvalues, but in the
table they explicitly claim that array names may not be lvalues. My
initial guess is that the second table would be expressions that could
not be *modifiable* lvalues.
 
C

Chris Smith

muttaa said:
Mr.Chris, will you please explain how array names can be an l-value?

It's a substantial portion of the rest of this thread. Apparently, the
C standard considers array names to be "unmodifiable lvalues" instead of
considering them not to be lvalues at all. Pete pointed out earlier in
the thread that this is an observable distinction because given an array
declared as "int b[10]" it is legal to ask for (&b), which is only
possible for an lvalue.

Note that in this way, the C standard's definition of lvalue is not
identical to common concept of lvalues in general programming languages,
which is that the expression may appear on the left-hand side of an
assignment operator. Clearly an array name is not an lvalue in the
classical sense. Specifications define their own language, and the C
standard chooses to define lvalue in one way, whereas it is commonly
used in other ways in practice.
 
S

SuperKoko

Zach said:
Chris said:
Sure. An l-value is an expression that may legally appear on the left-
hand side of the assignment operator, =. An r-value is an expression
that may appear on the right-hand side. The set of r-values is a subset
of the the set of l-values. For example, given:

int a;
int b[50];

The following are l-values (and also r-values):

a
b[10]

Whereas the following are ONLY r-values:

17
a + 5
b

Therefore, the following line of code will fail to compile:

a + 5 = 15;

Hope that helps,

Where are the rules of what may go on each side?
Since there is a lot of confusion in that discussion, I'll give rules
(based on C++).
First, I'll correct statements of Chris Smith:
int a;
int b[50];
a is an lvalue
b[10] is an lvalue
17 is an rvalue
a+5 is an rvalue
b *is an lvalue*

An lvalue designate an object (it may designate an inexistent or
invalid object), while an rvalue is a value itself...
In some sense, an lvalue is not a value, it is an object...

lvalues are not rvalues and rvalues are not lvalues... They are totally
different concepts.

Expressions can be rvalues or lvalues and take parameters... And
specific values are expected as arguments.
That is : there are three different contexts where an expression can
appear.
1) A context expecting an rvalue.
2) A context expecting an lvalue.
3) A context accepting both an rvalue or an lvalue (in that case,
semantics are slightly different depending on the type of the
expression).
This third category mainly exists in C++.

Any expression is either an lvalue or an rvalue.
If an lvalue-expression is found in a context where an rvalue is
expected, an rvalue-to-lvalue conversion occurs (and this conversion is
not conceptual... Physical things occur... And HERE can happen
segmentation faults).

If an rvalue is found in a context where an lvalue is expected, the
program is ill-formed.

Now, we can give the rule (C++ rules without operator overloading, but
AFAIK they are the same in C, except where I specify it explicitly).

string literals are lvalues. Other literals are rvalues.
An identifier identifying a variable (automatic or static, const or
not, volatile or not), or a function (yes) is an lvalue.
Additive (+ -), multiplicative (* / %), shift (<< >>), relationals (<
<= >= >), equality (== !=), bitwise (| & ^) and logical (|| &&)
operators expect rvalues operands and yields rvalues.

Postfix increment and decrement operators expect lvalues and yield
rvalues.

Assignment operators (= += *= etc) take a left lvalue operand and
expect a right rvalue operand and yield a rvalue in C, and an lvalue in
C++ (this is an incompatibility issue between C and C++).

Unary * operator :
It expects an rvalue operand and yields an lvalue which designates the
object pointed-to by the pointer.
The type can be incomplete, as far as this expression doesn't get an
lvalue-to-rvalue conversion.
The pointer can be NULL, as far as the expression doesn't get an
lvalue-to-rvalue conversion.

Unary & operator :
Expects an lvalue operand and yields an rvalue.

Arguments of functions expect rvalues (except if the parameter is a
reference type in C++)... What means that lvalue-to-rvalues conversion
occur if arguments are lvalues.

Function calls are rvalues (except if the returned type is a reference
type in C++).

Comma operator: It doesn't expect any particular type of value (i.e. it
accepts both lvalues and rvalues with different semantics).
The left operand can be either an rvalue or an lvalue, and is
discarded. If it is an lvalue, there is *no* lvalue-to-rvalue (nor
array-to-pointer, not function-to-pointer) conversion. In particular,
if p is a NULL pointer (*p , another_valid_expression) is valid and has
a well-defined behavior.
The right operand can either be an rvalue or an lvalue.
In C++, the resulting expression is an lvalue if (and only if) the
right operand is an lvalue, and has the same value than the right
operand, or is an rvalue if (and only if) the right operand is an
rvalue.
In C, AFAIK, the left operand can be either an rvalue or an lvalue and
the value is discarded (as in C++), but the right operand is expected
to be an rvalue, and the expression yields an rvalue.

Prefix increment and decrement operators are equivalent to +=1 and -=1
Thus, in C, they expect lvalue operands and yield rvalues, while in
C++, they expect lvalue operands and yield lvalues.

The dot operator (member access) for non-static data members (in C all
members are non-static data members) can accept an rvalue or an lvalue
(even in C, this operator enters in the third category).
If the operand is an lvalue, the expression is an lvalue.
If the operand is an rvalue, the expression is an rvalue.

p is equivalent to *(p + i) and thus, expect rvalue operands, and
yield an lvalue.
Similarly p->member is equivalent to (*p).member and expect an rvalue
operand and yield an lvalue.

conditional expressions (aka the ?: ternary operator) has also
different meanings in C and C++
It expects an rvalue as first operand (converted to bool in C++).
In C, it is quite simple : It expects rvalues second and third
operands, and yields an rvalue.
In C++ (with simplified rules), if both operands are of the same type
and are lvalues, the result is an lvalue.
If one operand can be implicitly converted to a reference to the other
type, the result is an lvalue.
If one operand has void data type, both operands are converted to void,
and get array-to-pointer, function-to-pointer and lvalue-to-rvalues
conversions, and the result is an rvalue (of void type).
Otherwise the result is an rvalue, and additional obfuscated rules
apply.
If you want to know all the C++ rules for conditional expressions, look
at:
http://www.open-std.org/jtc1/sc22/wg21/docs/wp/html/nov97-2/expr.html#expr.cond


Conversion expressions :
Expect an rvalue operand in C, and yields an rvalue in C.
In C++, they expect and yield an lvalue if the destination type is a
reference type.
From the C++ standard : an lvalue-to-rvalue conversion is:
"
4.1 Lvalue-to-rvalue conversion
[conv.lval]

1 An lvalue (_basic.lval_) of a non-function, non-array type T can
be
converted to an rvalue. If T is an incomplete type, a program
that
necessitates this conversion is ill-formed. If the object to
which
the lvalue refers is not an object of type T and is not an object of
a
type derived from T, or if the object is uninitialized, a program
that
necessitates this conversion has undefined behavior. If T is a
non-
class type, the type of the rvalue is the cv-unqualified version of
T.
Otherwise, the type of the rvalue is T. 1)

2 The value contained in the object indicated by the lvalue is
the
rvalue result. When an lvalue-to-rvalue conversion occurs within
the
operand of sizeof (_expr.sizeof_) the value contained in the
refer-
enced object is not accessed, since that operator does not
evaluate
its operand."


I would also like to explain where array-to-pointer conversion occurs.
Knowing that a variable whose type is an array is an lvalue (like any
other variable).

Whenever an lvalue array expression appears as an operand of an
operator that expects an rvalue for that operand, the array-to-pointer
conversion is applied to that operand.
Variadic arguments (i.e. arguments after an ellipsis) get
array-to-pointer conversions (and integer promotion for integer types).
In a conversion expression, if the result is an rvalue (i.e. the
destination type is not a reference type), then the left operand
expects an rvalue, and array-to-pointer conversion occur if the
parameter is of array type.
If the result of a conditional operator (?:) is an rvalue, then the
second and third operand are expected to be rvalues (and
array-to-pointer conversion occur).

One thing that confuse many programmers is the fact that an array can't
be assigned, can't be copy-constructed (in C++), can't be passed as
function parameter, and can't be returned from a function.
What is very confusing for programmers is that you can declare function
parameters which look like arrays but are pointers (good old pointer
automatic variables).
Old K&R C had similar limitations for structures.

If you read the C++ standard, you'll see that there are several places,
where it is explicitly stated that array-to-pointer,
function-to-pointer and lvalue-to-rvalue conversion does not occur!
It is stated in the standard in order to avoid incorrect
interpretations by programmers/compiler-vendors who tend to think that
there are such conversions where there are none.
There is *NO* array-to-pointer (nor lvalue-to-rvalue nor
function-to-pointer) conversion :
1) For the argument of typeid (in C++).
2) When a type is explicitly converted to void
3) On the operand of sizeof
From the C++ standard:
" 4.2 Array-to-pointer conversion
[conv.array]

1 An lvalue or rvalue of type "array of N T" or "array of unknown
bound
of T" can be converted to an rvalue of type "pointer to T."
The
result is a pointer to the first element of the array."

Now I hope that everybody will understand the basics of lvalues and
rvalues.
 

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

Members online

No members online now.

Forum statistics

Threads
473,769
Messages
2,569,582
Members
45,067
Latest member
HunterTere

Latest Threads

Top