L-values, r-values, expressions and types

Simple question: Why does this code compile?

image

…and this code doesn’t?

image

The compiler gives the following:

image

L-values

What is this ‘l-value’ thing? When (most of us) were taught C we were told an l-value is a value that can be placed on the left-hand-side of an assignment expression. However, that doesn’t give much of a clue as to what might constitute an l-value; so most of the time we resort to guessing and trial-and-error.

Basically, an l-value is a named object; which may be modifiable or non-modifiable. A named object is a region of memory you’ve given a symbolic name to variable (in human-speak: a variable). A literal has no name (it’s what we call a value-type) so that can’t be an l-value. A const variable is a non-modifiable l-value, but you can’t put them on the left-hand-side of an assignment. There fore our rule becomes:

Only modifiable l-values can go on the left-hand-side of an assignment statement.

R-values

An r-value, if we take the traditional view, is a value that can go on the right-hand-side of an assignment. The following compiles fine:

image

By our definition, literals are r-values. But what about the variable a? We defined it as a (non-modifiable) l-value, so are l-values also r-values? We need a better definition for r-values:

An r-value is an un-named object; or, if you prefer, a temporary variable.

An r-value can never go on the left-hand-side of an assignment since it will not exist beyond the end of the statement it is created in.

(As an aside: C++ differentiates between modifiable and non-modifiable r-values. C doesn’t make this distinction and treats all r-values as non-modifiable)

Expressions

Where do r-values come from? C is an expression-based language. An expression is a collection of operands and operators that yields a value. In fact, even an operand on its own is considered an expression (which yields its value). The value yielded by an expression is an object (a temporary variable) – an r-value.

Type of an expression

If an r-value is an object (variable) it must – since C is a typed language – must have a type. The type of an expression is the type of its r-value.

It’s worth having a look at some examples.

In the case of an assignment expression (y = x) the result of the expression is the value of the left-hand-side after assignment; the type is the type of the left-hand side.

If the expression is a logical operator (a != b) the result is an integer (an ‘effective Boolean’) with a value 0 or 1.

For mathematical expressions, the type of the expression is the same as the largest operand-type. So, for example:

image

C will promote, or convert, from smaller types to larger types automatically (if possible).

Finally, functions are considered r-values; with the type of the function being its return type. Thus, a function can never be placed on the right-hand-side of an assignment. There is a special-case exception to this: functions that take a pointer as a parameter, and then return that pointer as the return value, are considered l-values. For example:

image

(By the way, I don’t condone writing code like this!)

Back to the original question, then:

image

This compiles because c is a modifiable l-value; therefore can sit on the left-hand-side of an assignment. a + b is an expression that yields a (non-modifiable) r-value. Because of this, the code below cannot work:

image

And finally…

I’ll leave the following as an exercise for the reader (no prizes, though!)

Why does this compile:

image

…and this doesn’t:

image

Glennan Carnie

Glennan Carnie

Technical Consultant at Feabhas Ltd
Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry.

He specialises in C++, UML, software modelling, Systems Engineering and process development.
Glennan Carnie
Dislike (0)

About Glennan Carnie

Glennan is an embedded systems and software engineer with over 20 years experience, mostly in high-integrity systems for the defence and aerospace industry. He specialises in C++, UML, software modelling, Systems Engineering and process development.
This entry was posted in C/C++ Programming and tagged , , , , . Bookmark the permalink.

8 Responses to L-values, r-values, expressions and types

  1. dsr2008 says:

    Oops, It seems both do not compile:

    int main(void)
    {
    int a1 = 0;
    int b1 = 0;

    int a2 = 0;
    int b2 = 0;

    a1 = ++++b1;
    a2 = b2++++;

    return 0;
    }

    $ gcc test1.c
    test1.c: In function ‘main’:
    test1.c:10: error: lvalue required as increment operand
    test1.c:11: error: lvalue required as increment operand

    Got similar result on MSVC.

    Like (0)
    Dislike (1)
  2. Manish says:

    If there a typo in the blog entry:

    Should
    "An r-value can never go on the right-hand-side of an assignment since it will not exist beyond the end of the statement it is created in"

    read
    An r-value can never go on the left-hand-side of an assignment since it will not exist beyond the end of the statement it is created in

    Like (1)
    Dislike (0)
  3. dsr2008 says:

    Tried again. You are right when it compiles as C++ code.

    Like (0)
    Dislike (0)
  4. glennan says:

    Good catch, Manish. You're a star.

    Like (0)
    Dislike (0)
  5. glennan says:

    Correct. This code (a1 = ++++b1) in C, but will compile in C++.

    According to the C-99 Standard (ISO/IEC 9899:1999), Section 6.5.3.1 - The expression ++E is equivalent to (E += 1). By extension, ++++a1 should be (a1+=1)+=1 - which does compile.

    In C++03 (ISO/IEC 14882:2003), Section 5.3.2 - The operand shall be an l-value... ...The value is the new value of the operand: it is an l-value.

    Very interesting. Thanks for spotting that. If anything, it's yet another good justification for NOT writing code like ++++a1 🙂

    Like (0)
    Dislike (0)
  6. @Glennan: "...it’s yet another good justification for NOT writing code like ++++a1."

    Couldn't agree more.

    Going back to the principles, it's a pity the standards' people, when revising and/or clarifyng the meanings of r-value and l-value, missed the opportunity to come up with more appropriate names for them. The r- and l- designations came quite obviously from "right" and "left" and this is (now) shown to be naive, at best.

    Like (0)
    Dislike (0)
  7. Param says:

    "Thus, a function can never be placed on the right-hand-side of an assignment."

    But, we can have function on right hand side of an assignment

    int i;
    i =sum(10+20);

    Like (0)
    Dislike (0)
  8. Param says:

    typo
    int i;
    i =sum(10 ,20);

    Like (1)
    Dislike (0)

Leave a Reply