Lvalue vs Rvalue in C: Definitions and Grammar
In the C programming language, expressions are categorized based on whether they refer to a memory location or simply compute a transient value. An lvalue designates an object residing in a specific, identifiable memory address, while an rvalue refers to the data value itself produced by an expression. This distinction governs how expressions are evaluated, assigned, and manipulated within the syntactic grammar defined by the ISO C standard.
The Formal Grammar and Definitions
Historically, the terms originated from assignment syntax: an
lvalue appeared on the left side of an
assignment operator (=), and an rvalue
appeared on the right side.
In the formal C grammar (ISO/IEC 9899), the term "rvalue" is rarely used directly; instead, the standard refers to the "value of an expression." An lvalue is formally defined as an expression that potentially designates an object—where an "object" is a distinct region of data storage in the execution environment.
Syntactic Categories of Lvalues
An expression is an lvalue if it refers to a persistent memory location that can be read or modified. In C syntax, lvalues include:
- Identifiers: Variable names that designate objects
in storage (e.g.,
int x;wherexis an lvalue). - Pointer Indirection: The unary
*operator applied to a pointer expression (e.g.,*ptr). - Array Subscripts: The
[]operator evaluated as indirection (e.g.,arr[i], which is syntactically equivalent to*(arr + i)). - Member Access: The
.operator applied to an lvalue struct or union, or the->operator applied to a pointer expression (e.g.,point.xorptr->x). - String Literals: In C, string literals (such as
"hello") are statically allocated arrays ofchar, making them lvalues, although attempting to modify them results in undefined behavior.
Modifiable vs. Non-Modifiable Lvalues
The C grammar enforces that the left-hand operand of an assignment operator must be a modifiable lvalue. An lvalue is non-modifiable if:
- It possesses an array type.
- It possesses an incomplete type.
- It is qualified with the
consttype qualifier. - It is a
structorunioncontaining a member with aconstqualifier.
Syntactic Categories of Rvalues
An rvalue represents the evaluation of an expression that yields a value without binding that value to an accessible, named memory address. In C syntax, rvalues include:
- Numeric and Character Literals: Constants such as
42,3.14, and'a'. - Arithmetic and Bitwise Expressions: Operators that
compute intermediate results, such as
a + b,x * y, ori << 2. - Logical and Relational Expressions: Results of
comparisons, such as
a > borx && y. - Function Calls: Any function call returning a
non-void value (e.g.,
get_count()). - The Address-of Operator: The expression
&varyields an rvalue representing the address, even thoughvaritself is an lvalue. - Cast Expressions: Explicitly casting a type (e.g.,
(int)floating_val) yields an rvalue.
Grammar Constraints in Assignments
The C grammar defines an assignment expression as:
assignment-expression:
conditional-expression
unary-expression assignment-operator assignment-expression
Syntactically, the grammar requires a unary-expression
on the left side of the assignment operator. Semantically, the compiler
enforces that this unary-expression must resolve to a
modifiable lvalue.
An lvalue automatically undergoes an lvalue-to-rvalue conversion whenever it is used in a context that expects a value rather than an object designation (such as the right-hand operand of an assignment or an operand in an arithmetic expression). Conversely, an rvalue cannot be converted into an lvalue; it has no designated storage location that the programmer can reference via syntax.