Historically, in the C language, the distinction between lvalue and rvalue arose as a basis for determining to which expressions the assignment operator can be applied. Lvalue (left value) refers to an object that occupies a specific location in memory and to which the address-of operation (&) can be applied. Rvalue (right value) is a temporary value that does not have a specific address. This distinction is important for understanding how assignment works, how arguments are passed to functions, and how the compiler performs optimizations.
The problem arises when attempting to perform operations that are only valid on lvalues (e.g., assignment) on rvalues, and vice versa. This can lead to compilation errors or subtle bugs during execution.
The solution lies in a clear understanding of the contexts in which lvalues and rvalues are used. Example:
int x = 5; int y; y = x; // x is both lvalue and rvalue, y is lvalue y = x + 1; // x + 1 is rvalue (cannot take address) // &x is correct, &(x + 1) is an error
Key features:
Can you take the address of any expression, such as (x + y)?
No, only lvalues have an address. For example, the expression (x + y) is an rvalue.
int z = 3, y = 7; int *p = &(z + y); // Compilation error
What happens if you attempt to assign a value to a constant (e.g., 5 = x)?
A compilation error will occur because the literal 5 is an rvalue and cannot serve as an lvalue.
5 = x; // Error: left operand is not an lvalue
Can a function return an lvalue?
A regular function returns an rvalue, but if a reference is returned (for example, in C++), then it is an lvalue. In C, only rvalue returns are allowed.
int foo() { int x = 5; return x; } // Returns an rvalue
A programmer attempted to take the address of a temporary result of the expression (a + b):
int *p = &(a + b);
Pros:
Cons:
A programmer consciously separates lvalue and rvalue. They use values only where the syntax and semantics allow:
int x = 7; int *p = &x; // correct
Pros:
Cons: