Simple question: Why does this code compile?
…and this code doesn’t?
The compiler gives the following:
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.
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:
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)
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:
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 left-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:
(By the way, I don’t condone writing code like this!)
Back to the original question, then:
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:
I’ll leave the following as an exercise for the reader (no prizes, though!)
Why does this compile:
…and this doesn’t:
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.