An lvalue simply represents an item that can be located in memory (i.e., one with an address). Every assignment operation needs to support storing data in an "lvalue." Functions, expressions (like a+b), or constants (such as 3, 4, etc.) cannot function as lvalues.
The term "l-value" refers to a location in memory that serves to designate an object. An object can be identified on either side of the assignment operator (=) by an l-value. The concept of l-value is commonly employed for identification purposes. "Modifiable l-values" denote expressions that point to alterable locations. Arrays, incomplete types, or types marked with the const attribute are all ineligible to be modified as l-values. In the case of structures or unions, they must not contain any members with the const attribute to be considered editable l-values.
The value of the variable is the value that is stored at the location designated by the name of the identifier . If an identifier relates to a memory location and its type is arithmetic, structure, union , or pointer , it qualifies as a changeable lvalue . For instance, *ptr is a changeable l-value that identifies the storage region to which ptr points if ptr is a pointer to a storage region. Expressions that locate (designate) objects were given the new moniker of "locator value" in C. One of the following describes the l-value:
- Any variable's name, such as an identifier for an integral , floating-point, pointer, structure, or union type .
- An expression for the subscript () that does not evaluate an array.
- An expression with a unary indirection (*) that doesn't make reference to an array
- An enclosed l-value
- A const object (an l-value that cannot be changed).
- Indirection through a pointer that, if it's not a function pointer, produces the desired effect.
- Member access by pointer(-> or.) results in.
Example:
#include <stdio.h>
int main() {
int a; // declare 'a' as an object of type 'int'
int b;
// 'a' is an expression referring to an 'int' object as an l-value
a = 1;
b = a; // assigning the value of 'a' to 'b'
printf("The value of 'a' is %d\n", a);
printf("The value of 'b' is %d\n", b);
// The following line will cause a compilation error
// 9 = a;
return 0;
}
Output:
The value of 'a' is 1
The value of 'b' is 1
Explanation:
In this instance, integer variables a and b are initialized. The value 1 is assigned to variable a, followed by assigning the same value to variable b. Consequently, both variables a and b will contain the value 1.
Because an l-value is expected on the left side of the assignment operator (=), attempting to assign a value to a literal like 9 by writing 9 = a; will lead to a compilation error. This scenario is invalid because the literal 9 cannot be assigned a new value.
What is r-value?
R-value denotes an object lacking a specific address or memory location, resulting in a constant expression or value when used as a return value. Basic arithmetic operations, such as a+b, exemplify this behavior by generating a constant output.
The term "r-value" refers to a data item stored at a particular memory address. An r-value is an expression that is unable to be assigned a value, and therefore, it can only appear on the right side of the assignment operator (=), not on the left side.
Example:
#include <stdio.h>
int main() {
int a = 1, b;
int *p, *q;
// The following line will cause a compilation error
// a + 1 = b;
p = &a; // assigning the address of 'a' to 'p'
*p = 1; // assigning a value to the memory location pointed by 'p'
// The following line will cause a compilation error
// p + 2 = 18;
q = p + 5; // assigning the address calculated by 'p + 5' to 'q'
*(p + 2) = 18; // assigning a value to the memory location calculated by 'p + 2'
p = &b; // assigning the address of 'b' to 'p'
int arr[20];
arr[12] = 42; // assigning a value to the element at index 12 of 'arr'
struct S { int m; };
struct S obj;
struct S* ptr = &obj;
ptr->m = 24; // assigning a value to the member 'm' of the strucLogic Practiceed by 'ptr'
printf("The value of 'a' is %d\n", a);
printf("The value of 'b' is %d\n", b);
printf("The value at index 12 of 'arr' is %d\n", arr[12]);
printf("The value of 'obj.m' is %d\n", obj.m);
return 0;
}
Output:
The value of 'a' is 1
The value of 'b' is 0
The value at index 12 of 'arr' is 42
The value of 'obj.m' is 24
Explanation:
In this instance, the variables a, b, p, q, arr, obj, and ptr are all initialized. Various instances of assignments and operations with left- and right-hand values can be observed.
The result will showcase 1, 0, 42, and 24, correspondingly, for the variables a, b, the item at position 12 in the array, and the property m of the object.
Note: An l-value is needed as the operand for the unary & (address-of) operator. In other words, &n is only a legitimate expression if n is a l-value. Therefore, an expression like &12 is incorrect. Once more, 12 is not addressable because it does not refer to an object.
For example,
#include <stdio.h>
int main() {
int a, *p;
int x, y;
p = &a; // assigning the address of 'a' to 'p'
// The following line will cause a compilation error
// &a = p;
(x <y ? y : x) = 0; // assigning a value to either 'y' or 'x' based on the condition
printf("The value of 'a' is %d\n", a);
printf("The value of 'x' is %d\n", x);
printf("The value of 'y' is %d\n", y);
return 0;
}
Output:
The value of 'a' is undefined (garbage value)
The value of 'x' is undefined (garbage value)
The value of 'y' is undefined (garbage value)
Explanation:
In this instance, variables a, p, x, and y are defined. The assignment operator (=) is employed to grant p access to the memory address of a. Nonetheless, as the address of a (&a) is considered an r-value and is immutable, attempting to assign a new value to it with the expression &a = p; will lead to a compilation error.
The ternary operator is employed in the line (x < y ? y : x) = 0. This operator results in an l-value, allowing us to assign a value of 0 to either y or x based on the condition (x < y).