According to "C - A Reference Manual", an lvalue is an expression that refers to an object in such a way that the object may be examined or altered.
For example,
int ranks[10];
Then are ranks and ranks+3 lvalues? Since ranks(pointer to first element of array) and ranks+3(pointer to fourth element of array) refer to objects, I guess they are lvalues -- or are they not lvalues?
ranks+3 is not an lvalue for the same reason that 1+3 is not an lvalue; the result is simply a numerical (pointer) value, not a reference to an object.
Put another way, if the result of ranks + 3 is 0xdeadbeef, you cannot write
0xdeadbeef = 10;
for the same reason you cannot write
4 = 10;
However, *(ranks+3) is an lvalue, because you are dereferencing that pointer value; it is the 4th element of the ranks array, and is equivalent to ranks[3].
Similarly, ranks is not an lvalue except in the context of sizeof or the unary & operator. Since array expressions "decay" to pointer expressions in most contexts, you again wind up with the situation of
ranks = 10;
being equivalent to
0xdeadbeef = 10;
Again, you'd have to dereference ranks as either *ranks or ranks[0] for it to be an lvalue.
In this context, the names aren't lvalues, but they can be dereferenced with * to form lvalues. For example:
int ranks[10];
// ranks = 42; <--- doesn't compile! error: assignment to expression with array type
*ranks = 42; // OK: same as ranks[0] = 42
//ranks+3 = 42; <--- error: lvalue required as left operand of assignment
*(ranks+3) = 42; //OK: same as ranks[3] = 42
This answer lists the few cases in C when the name of an array doesn't decay to a pointer to the array's first element. On the left-hand side of an assignment isn't one of those cases. Therefore, in ranks = ..., ranks is (decays to, acts as) a pointer to the first element of the array. You have to dereference that pointer (*ranks) to get one of the array elements as an lvalue.
Just don't ask me what an lvalue is. I know it when I see it. :)
Tested on gcc.
Related
Assume following Code:
#include <stdio.h>
#include <stdlib.h>
int main (int argc, char **argv)
{
int arrayXYZ[10];
int i;
int *pi;
int intVar;
for (i=0; i<10; i++){
arrayXYZ[i] = i;
}
pi = arrayXYZ; // Reference 1
pi++; // Reference 2
arrayXYZ++; // Reference 3
arrayXYZ = pi; // Reference 4
}
Reference 1 is correct: pi points to first element in arrayXYZ -> *pi = 0
Reference 2 is correct: element to which pi points is incremented -> *pi = 1
Reference 3 is not correct: I am not completely sure why. Every integer needs 4 bits of memory. Hence, we cannot increment the address of the head of the array by just one? Assume, we had a char array with sizeof(char)=1 -> Would the head of the array point to the next bucket?
Reference 4 is not correct: I am not completely sure why. Why cannot the head of the array point to the address to which pi points?
Thanks for all clarifications!
I am a new member, so if my question doesn't follow the Stackoverflow guidelines, feel free to tell me how I can improve my next questions!
arrayXYZ++;
This is equivalent to:
arrayXYZ += 1;
which is equivalent to:
arrayXYZ = arrayXYZ + 1;
This is not allowed because the C language does not allow it. An array can not be assigned to.
arrayXYZ = pi;
This fails for the same reason. An array can not be assigned to.
The other assignments work because you are allowed to assign to a pointer.
Also keep in mind that arrays and pointers are distinct datatypes. In C, there are circumstances where arrays decay into a pointer to their first element for convenience purposes. Which is why this works:
pi = arrayXYZ;
However, this is just an automatic conversion, so that you don't have to write:
pi = &arrayXYZ[0];
This automatic conversion does not mean that arrays are the same thing as pointers.
From C11 standard §6.3.2.1 (N1570)
An lvalue is an expression (with an object type other than void) that potentially designates an object;64) if an lvalue does not designate an object when it is evaluated, the behavior is undefined. When an object is said to have a particular type, the type is specified by the lvalue used to designate the object. A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, does not have a const- qualified type, and if it is a structure or union, does not have any member (including, recursively, any member or element of all contained aggregates or unions) with a const- qualified type.
And also From §6.5.2.4
The operand of the postfix increment or decrement operator shall have atomic, qualified, or unqualified real or pointer type, and shall be a modifiable lvalue.
As pointed out here these are the reasons why those statements are illegal. Same way for assignment operation the left one has to be modifiable. Here it is not. That's why the problem.
Now to explain why the other two works - there is a thing called array decay. Array in most situations (exceptions are when used in operand of &, sizeof etc) are converted to pointer to the first element of the array and that pointer is being assigned to the pi. This is modifiable. And that's why you can easily apply ++ over it.
For example,
int x[10];
int i = 0;
x = &i; //error occurs!
According to C - A Reference Manual, an array name cannot be an lvalue. Thus, x cannot be an lvalue. But, what is the reason the array name cannot be an lvalue? For example, why does an error occur in the third line?
Your reference is incorrect. An array can be an lvalue (but not a modifiable lvalue), and an "array name" (identifier) is always an lvalue.
Take your example:
int x[10];
int i = 0;
x = &i; //error occurs!
Apply C11 6.5.1, paragraph 2:
An identifier is a primary expression, provided it has been declared
as designating an object (in which case it is an lvalue) ...
We see that x is a primary expression and is an lvalue, because it has previously been declared as designating an array object.
However, the C language rules state that an array expression in various contexts, including the left-hand-side of an assignment expression, are converted to a pointer which points at the first element of the array and is not an lvalue, even if the array was. Specifically:
Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
(C11 6.3.2.1 paragraph 3).
The pointer which is the result of the conversion specified above is not an lvalue because an lvalue designates an object, and there is no suitable object holding the pointer value; the array object holds the elements of the array, not a pointer to those elements.
The example you use in your question implies that you understand that an array expression decays (is converted to) a pointer value, but I think you are failing to recognize that after the conversion, the pointer value and the array are two different things. The pointer is not an lvalue; the array might be (and in your example, it is). Whether or not arrays are lvalues in fact has no bearing on your example; it is the pointer value that you are trying to assign to.
If you were to ask instead: Why do arrays decay to pointers when they are on the left-hand-side of an assignment operator? - then I suspect that there is no particularly good answer. C just doesn't allow assignment to arrays, historically.
Array names are non-modifiable lvalues in C.:)
Arrays are named extents of memory where their elements are placed. So you may not substitute one extent of memory for another extent of memory. Each extent of memory initially allocated for an array declaration has its own unique name. Each array name is bounded with its own extent of memory.
An array is an lvalue, however it is a non-modifiable lvalue.
It most likely has to do with compatibility of types. For example, you can do this:
struct ss {
char c[10];
};
...
struct ss s1 = { { "hello" } };
struct ss s2 = s1;
But not this:
char s1[10] = "hello";
char s2[10] = s1;
It's true that array names yield pointer values in many contexts. But so does the & operator, and you don't expect that to be assignable.
int i = 42;
int *j = malloc(sizeof *j);
&i = j; /* obviously wrong */
int a[] = {1,2,3};
&a[0] = j; /* also obviously wrong */
a = j; /* same as the previous line! */
So when learning the relationship between arrays and pointers, remember that a is usually the same as &a[0] and then you won't think lvalue-ness is an exception to the rule - it follows the rule perfectly.
In K&R's The C Programming Language's 5.3 Pointers and Arrays
A
pointer is a variable, so pa=a and pa++ are legal. But an array name is not a variable;
constructions like a=pa and a++ are illegal.
In https://www.oreilly.com/library/view/understanding-and-using/9781449344535/ch04.html#DifferencesBetweenArraysAndPointersSection:
The pointer pv is an lvalue. An lvalue denotes the term used on the
lefthand side of an assignment operator. An lvalue must be capable of
being modified. An array name such as vector is not an lvalue and
cannot be modified. The address assigned to an array cannot be changed
. A pointer can be assigned a new value and reference a different
section of memory.
Consider the following:
pv = pv + 1;
vector = vector + 1; // Syntax error
We cannot modify vector, only its contents. However, the expression
vector+1 is fine, as demonstrated below:
pv = vector + 1;
If an array name is not a variable and not a lvalue, what is an array name? A constant?
An array is not an assignable lvalue, which means it can't appear on the left side of an assignment or otherwise be modified. Array elements, however, are assignable lvalues.
EDIT:
An lvalue has an address and can be referenced in multiple places. So something like const int foo = 12; is still an lvalue, but not a modifiable one.
In contrast, a constant such as 7 or "hello" is an rvalue, meaning it cannot be referenced beyond that expression. Expressions such as a + b or a + 1 are also rvalues.
I don't understand the results of following code:
#include <stdio.h>
#include <conio.h>
int main()
{
int a[4]={1, 3, 5, 6};
//suppose a is stored at location 2010
printf("%d\n", a + 2);
printf("%d", a++);
return 0;
}
Why does the second printf function produce following error?
error: lvalue required as increment operand
Part-1:
Array names are constant (not modifiable lvalue), your can add value to array name but can't modify it.
Expression a + 2 doesn't modify a itself but when you do a++ that is equivalent to a = a + 1 try to modify array name --lvalue error. The expression a++ in second printf is wrong - an example of semantic phase error. read following language standards:
6.3.2.1 Lvalues, arrays, and function designators
724 A modifiable lvalue is an lvalue that does not have array type,
does not have an incomplete type, does not have a const-qualified
type, and if it is a structure or union, does not have any member
(including, recursively, any member or element of all contained
aggregates or unions) with a const-qualified type.
729 Except when it is the operand of the sizeof operator or the unary & operator,
or is a string literal used to initialize an array, an expression that
has type “array of type” is converted to an expression with type
“pointer to type” that points to the initial element of the array
object and is not an lvalue.
Part-2:
Note array names in most expressions decays in address of first element (read some exceptions where array name not decaying into a pointer to first element? ably answered by #H2CO3).
When you do a + 2 its result is address of third element (or address of element at index 2) So a + 2 is same as &a[2] It is address not value at index.
To print address use %p instead of %d and typecast address into void* as follows:
printf("address (a + 2) = %p , &a[2] = %p", (void*)(a + 2), (void*)(&a[2]));
To print value you need defence operator * as follows:
printf("address *(a + 2) = %d , a[2] = %d", *(a + 2), a[2]);
Part-3:
suppose a is stored at location 2010, Is the output of first printf function 2012?
No, pointer arithmetic is different then integer arithmetic. As we know array name decays into address of first element's address in most expressions So when you do a + 2 the value is address of third element that is at index 2. So suppose if int size in your system is 4 bytes then a + 2 stat pointing to location 2018 according to your assumption that a address value is 2010.
To understand read 10.2 Pointers and Arrays; Pointer Arithmetic and Pointer Arithmetic.
int a[4]={1,3,5,6};
printf("%d\n",a++); // you should not modify array name
illegal in c
Assume pa is integer pointer
A pointer is a variable, so pa=a and pa++ are legal. But an array name is not a
variable; constructions like a=pa and a++ are illegal.
I think the first output will be depedent on how the integer type is represented in your computer. If a single integer occupies 4-bytes in memory, the output should be 2018, i.e. 2010+2*4. The second printf can cause a compilation error.
First this program invokes undefined behavior and I am little discouraged that with so many answers not one of them mentions this. In both your printf calls your argument is a pointer yet your are specifying the format as %d which expects and int it should be %p. The C99 draft standard in section 7.19.6.1 The fprintf function which printf's section refers back to for the format string paragraph 9 says:
If a conversion specification is invalid, the behavior is undefined.[...]
Back to your question, the a++ expression produces an error because postfix increment requires that it's operand is a modifiable lvalue, the draft standard in section 6.5.2.4 Postfix increment and decrement operators paragraph 1 says(emphasis mine):
The operand of the postfix increment or decrement operator shall have qualified or
unqualified real or pointer type and shall be a modifiable lvalue.
we can see from setion 6.3.2.1 values, arrays, and function designators paragraph 1 says:
[...]A modifiable lvalue is an lvalue that does not have array type[...]
The name of array is a constant pointer and so it will always point to the 0th element of that array. It is not a variable so nor can we assign some other address to it neither can we move it by incrementing or decrementing.
Hence
a = &var; /*Illegal*/
a++; /*Illegal*/
a = a-1; /*Illegal*/
Array memory addresses remain constant, so you cannot change it. That's what you are doing in a++ statement. So compiler will throw error.
a is not a variable of int type, it's a pointer to integer, so to print it you need to dereference it first
printf("%d\n", *(a + 2));
Why can't I do p=numbers++ or p=++numbers? The compiler shows the message: "lvalue required as increment operand" but isn't the pointer p a left value?
int main(int argc, char *argv[]){
int numbers[] = {1,2,3,4,5,6}, *p;
p=numbers++;
printf("%d ",*p);
return 0;
}
When you declare and define an array, the name of the array is an expression that evaluates to the address of the first element of the array. Think of the name as a constant that holds the address of the first element.
Once memory has been allocated for the array, the address of the array cannot be changed. Consequently, the value of the array identifier (name) cannot be changed either.
In your code, when you have
p = numbers++;
you are asking that numbers, which has the constant value of the address of the first element of the array, be incremented and point to the second element instead. This is not a legal operation. The increment operand requires a modifiable lvalue, and the name of the array is not a modifiable lvalue.
What is an lvalue? It is a locator value: something that identifies an area of memory that can hold another value. So when you declare:
int a = 5;
a signifies an area of memory large enough to hold an int, and the int held in that area of memory is 5. You can change that int by assignment:
a = 7;
but the identifier a still signifies the same area of memory. This means a is a modifiable lvalue.
When you declare and define your array:
int numbers[] = { 1, 2, 3, 4, 5, 6 };
numbers is an lvalue, in that it specifies an area of memory that holds the array of specified integers, but it is not modifiable. You can change the value of the elements in numbers, but you cannot change the value of numbers itself. numbers always evaluates to &numbers[0].
If you want to change where p points so that it points to the second element instead of the first, use:
p = numbers + 1;
This does not change the value of numbers itself, only that of p. numbers is still &numbers[0] and p will be &numbers[1].
Hope this is clear!
numbers++ is the equivalent of numbers = numbers + 1
However, numbers here is an array and you cannot modify the address of an array.
lvalue doesn't mean a "left value"
First I recommend that you read the C FAQ's section on Arrays and Pointers it is one of the better general references.
To directly address your error, if we look at the draft C99 standard we see that in sections 6.5.2.4 Postfix increment and decrement operators and
6.5.3.1 Prefix increment and decrement operators say in paragraph 1:
The operand of the prefix increment or decrement operator shall have qualified or
unqualified real or pointer type and shall be a modifiable lvalue.
section 6.3.2.1 Lvalues, arrays, and function designators paragraph 3 says:
Except when it is the operand of the sizeof operator or the unary & operator, or is a
string literal used to initialize an array, an expression that has type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue. [...]
and paragraph 1 says:
[...] A modifiable lvalue is an lvalue that does not have array type, [...]
so even though an array will decay to a pointer to the first element in many circumstances it is not a lvalue.
numbers is NOT a pointer. It is an array with this definition int numbers[] = {1,2,3,4,5,6}, and a rvalue. So the compiler shows the message: "lvalue required as increment operand".
Difference between array and pointer. Pointers - Difference between Array and Pointer