Array increment operator in C - c

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));

Related

Are these pointer lvalues?

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.

casting pointer to array into pointer

Consider the following C code:
int arr[2] = {0, 0};
int *ptr = (int*)&arr;
ptr[0] = 5;
printf("%d\n", arr[0]);
Now, it is clear that the code prints 5 on common compilers. However, can somebody find the relevant sections in the C standard that specifies that the code does in fact work? Or is the code undefined behaviour?
What I'm essentially asking is why &arr when casted into void * is the same as arr when casted into void *? Because I believe the code is equivalent to:
int arr[2] = {0, 0};
int *ptr = (int*)(void*)&arr;
ptr[0] = 5;
printf("%d\n", arr[0]);
I invented the example while thinking about the question here: Pointer-to-array overlapping end of array ...but this is clearly a distinct question.
For unions and structures, cf. ISO 9899:2011§6.7.2.1/16f:
16 The size of a union is sufficient to contain the largest of its members. The value of at most one of the members can be stored in a union object at any time. A pointer to a union object, suitably converted, points to each of its members (or if a member is a bit-field, then to the unit in which it resides), and vice versa.
17 Within a structure object, the non-bit-field members and the units in which bit-fields reside have addresses that increase in the order in which they are declared. A pointer to a structure object, suitably converted, points to its initial member (or if that member is a bit-field, then to the unit in which it resides), and vice versa. There may be unnamed padding within a structure object, but not at its beginning.
For array types, the situation is a bit more complex. First, observe what an array is, from ISO 9899:2011§6.2.5/20:
An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. The element type shall be complete whenever the array type is specified. Array types are characterized by their element type and by the number of elements in the array. An array type is said to be derived from its element type, and if its element type is T, the array type is sometimes
called “array of T”. The construction of an array type from an element type is called “array type derivation”.
The wording “contiguously allocated” implies that there is no padding between array members. This notion is affirmed by footnote 109:
Two objects may be adjacent in memory because they are adjacent elements of a larger array or adjacent members of a structure with no padding between them, or because the implementation chose to place them so, even though they are unrelated. If prior invalid pointer operations (such as accesses outside array bounds) produced undefined behavior, subsequent comparisons also produce undefined behavior.
The use of the sizeof operator in §6.5.3.5, Example 2 expresses the intent that there is also no padding before or after arrays:
EXAMPLE 2
Another use of the sizeof operator is to compute the number of elements
in an array:
sizeof array / sizeof array[0]
I therefore conclude that a pointer to an array, converted to a pointer to the element typo of that array, points to the first element in the array. Furthermore, observe what the definition of equality says about pointers (§6.5.9/6f.):
6 Two pointers compare equal if and only if both are null pointers, both are pointers to the same object (including a pointer to an object and a subobject at its beginning) or function, both are pointers to one past the last element of the same array object, or one is a pointer
to one past the end of one array object and the other is a pointer to the start of a different array object that happens to immediately follow the first array object in the address space.109)
7 For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type.
Since the first element of an array is “a subobject at its beginning,” a pointer to the first element of an array and a pointer to an array compare equal.
Here is a slightly refactored version of your code for easier reference:
int arr[2] = { 0, 0 };
int *p1 = &arr[0];
int *p2 = (int *)&arr;
with the question being: Is p1 == p2 true, or unspecified, or UB?
Firstly: I think that it is intended by the authors of C's abstract memory model that p1 == p2 is true; and if the Standard doesn't actually spell it out then it would be a defect in the Standard.
Moving on; the only relevant piece of text seems to be C11 6.3.2.3/7 (irrelevant text excised):
A pointer to an object type may be converted to a pointer to a different object type. [...] When converted back again, the result shall compare equal to the original pointer.
When a pointer to an object is converted to a pointer to a character type, the result points to the lowest addressed byte of the object. Successive increments of the result, up to the size of the object, yield pointers to the remaining bytes of the object.
It doesn't specifically say what the result of the first conversion is. Ideally it should say ...and the pointer points to the same address, but it doesn't.
However, I argue that it it is implied that the pointer must point to the same address after the conversion. Here is an illustrative example:
void *v1 = malloc( sizeof(int) );
int *i1 = (int *)v1;
If we do not accept "and the pointer points to the same address" then i1 might not actually point into the malloc'd space, which would be ridiculous.
My conclusion is that we should read 6.3.2.3/7 as saying that the pointer cast does not change the address being pointed to. The part about using pointers to character type seems to back this up.
Therefore, since p1 and p2 have the same type and point to the same address, they compare equal.
To answer directly:
Can somebody find the relevant sections in the C standard that specifies that the code does in fact work?
6.3.2.1 Lvalues, arrays, and function designators, paragraph 1
6.3.2.3 Pointers, paragraphs 1,5 and 6
6.5.3.2 Address and indirection operators, paragraph 3
Or is the code undefined behaviour?
The code you posted is not undefined, but it "might" be compiler/implementation specific (per section 6.3.2.3 p5/6)
What I'm essentially asking is why &arr when casted into void * is the same as arr when casted into void *?
This would imply asking why int *ptr = (int*)(void*)&arr gives the same results as int *ptr = (int*)(void*)arr;, but per your code posted, you're actually asking why int *ptr = (int*)(void*)&arr gives the same as int *ptr = (int*)&arr.
Either way I'll expand on what your code is actually doing to help clarify:
Per 6.3.2.1p3:
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.
and per 6.5.3.2p3:
The unary & operator yields the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’.
So in your first declaration
int arr[2] = {0, 0};
arr is initialized to an array type containing 2 elements of type int both equal to 0. Then per 6.3.2.1p3 it is "decayed" into a pointer type pointing to the first element anywhere it is called in scope (except when it's used like sizeof(arr), &arr, ++arr or --arr).
So in your next line, you could simply just do the following:
int *ptr = arr; or int *ptr = &*arr; or int *ptr = &arr[0];
and ptr is now a pointer to an int type that points to the first element of the array arr (i.e. &arr[0]).
Instead you declare it as such:
int *ptr = (int*)&arr;
Lets break this down into it's parts:
&arr -> triggers the exception to 6.3.2.1p3 so instead of getting &arr[0], you get the address to arr which is an int(*)[2] type (not an int* type), so you are not getting a pointer to an int, you are getting a pointer to an int array
(int*)&arr, (i.e. the cast to int*) -> per 6.5.3.2p3, &arr takes the address of the variable arr returning a pointer to the type of it, so simply saying int* ptr = &arr will give a warning of "incompatible pointer types" (since ptr is of type int* and &arr is of type int(*)[2]) which is why you need to cast to an int*.
Further per 6.3.2.3p1: "a pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer".
So, you're declaration of int* ptr = (int*)(void*)&arr; would produce the same results as int* ptr = (int*)&arr; because of the types you are using and converting to/from. Also as a note: ptr[0] = 5; is the same as *ptr = 5, where ptr[1] = 5; would also be the same as *++ptr = 5;
Some of the references:
6.3.2.1 Lvalues, arrays, and function designators
1. An lvalue is an expression (with an object type other than void) that potentially designates an object (*see note); 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 constqualified 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 constqualified type.
*The name ‘‘lvalue’’ comes originally from the assignment expression E1 = E2, in which the left operand E1 is required to be a (modifiable) lvalue. It is perhaps better considered as representing an object ‘‘locator value’’. What is sometimes called ‘‘rvalue’’ is in this International Standard described as the ‘‘value of an expression’’. An obvious example of an lvalue is an identifier of an object. As a further example, if E is a unary expression that is a pointer to an object, *E is an lvalue that designates the object to which E points.
2. Except when it is the operand of the sizeof operator, the _Alignof operator, the unary & operator, the ++ operator, the -- operator, or the left operand of the . operator or an assignment operator, an lvalue that does not have array type is converted to the value stored in the designated object (and is no longer an lvalue); this is called lvalue conversion. If the lvalue has qualified type, the value has the unqualified version of the type of the lvalue; additionally, if the lvalue has atomic type, the value has the non-atomic version of the type of the lvalue; otherwise, the value has the type of the lvalue. If the lvalue has an incomplete type and does not have array type, the behavior is undefined. If the lvalue designates an object of automatic storage duration that could have been declared with the register storage class (never had its address taken), and that object is uninitialized (not declared with an initializer and no assignment to it has been performed prior to use), the behavior is undefined.
3. 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.
6.3.2.3 Pointers
1. A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
5. An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation (the mapping functions for converting a pointer to an integer or an integer to a pointer are intended to be consistent with the addressing structure of the execution environment).
6. Any pointer type may be converted to an integer type. Except as previously specified, the result is implementation-defined. If the result cannot be represented in the integer type, the behavior is undefined. The result need not be in the range of values of any integer type.
6.5.3.2 Address and indirection operators
1. The operand of the unary & operator shall be either a function designator, the result of a [] or unary * operator, or an lvalue that designates an object that is not a bit-field and is not declared with the register storage-class specifier.
3. The unary & operator yields the address of its operand. If the operand has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the operand is the result of a unary * operator, neither that operator nor the & operator is evaluated and the result is as if both were omitted, except that the constraints on the operators still apply and the result is not an lvalue. Similarly, if the operand is the result of a [] operator, neither the & operator nor the unary * that is implied by the [] is evaluated and the result is as if the & operator were removed and the [] operator were changed to a + operator. Otherwise, the result is a pointer to the object or function designated by its operand.
4. The unary * operator denotes indirection. If the operand points to a function, the result is a function designator; if it points to an object, the result is an lvalue designating the object. If the operand has type ‘‘pointer to type’’, the result has type ‘‘type’’. If an invalid value has been assigned to the pointer, the behavior of the unary * operator is undefined (*see note).
*Thus, &*E is equivalent to E (even if E is a null pointer), and &(E1[E2]) to ((E1)+(E2)). It is always true that if E is a function designator or an lvalue that is a valid operand of the unary & operator, *&E is a function designator or an lvalue equal to E. If *P is an lvalue and T is the name of an object pointer type, *(T)P is an lvalue that has a type compatible with that to which T points. Among the invalid values for dereferencing a pointer by the unary * operator are a null pointer, an address inappropriately aligned for the type of object pointed to, and the address of an object after the end of its lifetime.
6.5.4 Cast operators
5. Preceding an expression by a parenthesized type name converts the value of the expression to the named type. This construction is called a cast (a cast does not yield an lvalue; thus, a cast to a qualified type has the same effect as a cast to the unqualified version of the type). A cast that specifies no conversion has no effect on the type or value of an expression.
6. If the value of the expression is represented with greater range or precision than required by the type named by the cast (6.3.1.8), then the cast specifies a conversion even if the type of the expression is the same as the named type and removes any extra range and precision.
6.5.16.1 Simple assignment
2. In simple assignment (=), the value of the right operand is converted to the type of the assignment expression and replaces the value stored in the object designated by the left operand.
6.7.6.2 Array declarators
1. In addition to optional type qualifiers and the keyword static, the [ and ] may delimit an expression or *. If they delimit an expression (which specifies the size of an array), the expression shall have an integer type. If the expression is a constant expression, it shall have a value greater than zero. The element type shall not be an incomplete or function type. The optional type qualifiers and the keyword static shall appear only in a declaration of a function parameter with an array type, and then only in the outermost array type derivation.
3. If, in the declaration ‘‘T D1’’, D1 has one of the forms:
D[ type-qualifier-listopt assignment-expressionopt ]
D[ static type-qualifier-listopt assignment-expression ]
D[ type-qualifier-list static assignment-expression ]
D[ type-qualifier-listopt * ]
and the type specified for ident in the declaration ‘‘T D’’ is ‘‘derived-declarator-type-list T’’, then the type specified for ident is ‘‘derived-declarator-type-list array of T’’.142) (See 6.7.6.3 for the meaning of the optional type qualifiers and the keyword static.)
4. If the size is not present, the array type is an incomplete type. If the size is * instead of being an expression, the array type is a variable length array type of unspecified size, which can only be used in declarations or type names with function prototype scope;143) such arrays are nonetheless complete types. If the size is an integer constant expression and the element type has a known constant size, the array type is not a variable length array type; otherwise, the array type is a variable length array type. (Variable length arrays are a conditional feature that implementations need not support; see 6.10.8.3.)
5. If the size is an expression that is not an integer constant expression: if it occurs in a declaration at function prototype scope, it is treated as if it were replaced by *; otherwise, each time it is evaluated it shall have a value greater than zero. The size of each instance of a variable length array type does not change during its lifetime. Where a size expression is part of the operand of a sizeof operator and changing the value of the size expression would not affect the result of the operator, it is unspecified whether or not the size expression is evaluated.
6. For two array types to be compatible, both shall have compatible element types, and if both size specifiers are present, and are integer constant expressions, then both size specifiers shall have the same constant value. If the two array types are used in a context which requires them to be compatible, it is undefined behavior if the two size specifiers evaluate to unequal values.
P.S. As a side note, given the following code:
#include <stdio.h>
int main(int argc, char** argv)
{
int arr[2] = {10, 20};
X
Y
printf("%d,%d\n", arr[0],arr[1]);
return 0;
}
where X was one of the following:
int *ptr = (int*)(void*)&arr;
int *ptr = (int*)&arr;
int *ptr = &arr[0];
and Y was one of the following:
ptr[0] = 15;
*ptr = 15;
When compiled on OpenBSD with gcc version 4.2.1 20070719 and providing the -S flag, the assembler output for all files was exactly the same.

What does address of a, which is an array, returns?

I thought when you try to get the address of an array, it returns the address of the first element it holds.
int *j;
int a[5]={1,5,4,7,8};
Now j=&a[0]; works perfectly fine.
Even j=a also does the same function.
But when I do j=&a it throws an error saying cannot convertint (*)[5]' to int*' in assignment
Why does it happen? &a should be the first element of the array a, so it should give &a[0].
But instead it throws an error. Can somebody explain why?
The C standard says the following regarding how arrays are used in expressions (taken from C99 6.3.2.1/3 "Lvalues, array, and function designators):
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
This is commonly known as "arrays decay to pointers".
So the sub-expression a in the following larger expressions evaluates to a pointer to int:
j=&a[0]
j=a
In the simpler expression, j=a, that pointer is simply assigned to j.
In the more complex expression, j=&a[0], the 'index' operator [] is applied to the pointer (which is an operation equivalent to *(a + 0)) and the 'address-of' operator is applied to that, resulting in another pointer to int that gets assigned to j.
In the expression j=&a, the address-of operator is applied directly to the array name, and we hit one of the exceptions in the above quoted clause: "Except when it is the operand of ... the unary & operator".
Now when we look at what the standard says about the unary & (address-of) operator (C99 6.5.3.2/3 "Address and indirection operators"):
The unary & operator returns the address of its operand. If the
operand has type "type", the result has type "pointer to type".
Since a has type "array of 5 int" (int [5]), the result of applying & to it directly has type "pointer to array of 5 int" (int (*)[5]), which is not assignable to int*.
The type of a and &a is not the same even though they contain the same value, i.e., base address of the array a.
j = a;
The array name a here gets converted to a pointer to its first element.
Try to see what values you get via these statements to understand where the difference lies:
printf("%p", a+1);
printf("%p", &a+1);
c is a strongly typed language. Assignment such as j=a; is allowed only if j and a are of the same type or the compiler can safely convert a to j. In your case, type of j is int * while the type of &a is int (*)[5]. The compiler does not know how to automatically convert an object of type int (*)[5] to an object of type int *. The compiler is telling you exactly that.
a is an array of 5 ints. The pointer to a is a pointer to an array of five integers, or int (*)[5]. This is not compatible with an int * because of pointer arithmetic: If you increment a variable of type int *, the address in the variable increases by 4 (assuming 4 byte integers), so that it points to the next integer. If you increment a variable that points to an array of 5 integers, the address in the variable increases by 20 (again assuming 4 byte integers), so that it points to the next array of five integers.
Perhaps what's confusing is that the value give by a and &a is the same, as you said. The value is the same but the type is different, and the difference is most obvious when you do arithmetic on the pointers.
I hope that helps.

Increment a vector adress and assign it to a pointer

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

Does the array-to-pointer conversion annihilate the evaluation of the indirection operator?

In Find size of array without using sizeof, the size of an array is computed via
int arr[100];
printf ("%td", (&arr)[1] - arr);
Now, for the purposes of pointer arithmetic, arr is considered the element of a single-element array, so
&arr + 1
is a pointer one-past the end of that (conceptual) single-element array, so when the address of arr[0] is subtracted, the number of elements in arr is obtained.
In (&arr)[1], that pointer is the operand of the indirection operator,
(&arr)[1] ≡ *(&arr + 1)
and the resulting array expression is then converted to an int* as per 6.3.2.1 (3).
So far, so good. But the last sentence in 6.5.6 (8) (Additive operators),
If the result points one past the last element of the array object, it
shall not be used as the operand of a unary * operator that is evaluated.
forbids the evaluation of the indirection operator there.
The question is whether the indirection operator is evaluated in
*(&arr + 1) - arr
(in which case that expression would invoke undefined behaviour) or the array-to-pointer conversion annihilates the evaluation (in which case all is well), like taking the address (&(*(&arr + 1))) or applying sizeof to it would..
I think the standard is pretty clear that the * operator is evaluated here. The result of the evaluation is an lvalue which is never converted to a value, but the result of the evaluation is used nonetheless.
There is a special exception when the address is immediately taken using the & operator: in &*x, where x has type T *, x is evaluated, but the * and & operators are not. There is no intermediate lvalue of type T at runtime of which the address is taken. So &*x is valid even when x is a null pointer or one past the end of an array.
Since you are not using the & operator to take the address, that exception does not apply. There is no similar exception for the array-to-pointer conversion, so even when x is an array type, *x is invalid if x does not actually point to any object.
You want to do this: For any T x;, it is true that &x + 1 is the address just past the end of x, and numerically it is a pointer that is obtained by incrementing &x by sizeof(T) (so the sizeof is still there!).
Now if T = U[N], then x is an array of N elements of type U. But the pointer to such an array is also a pointer to the first element of such an array. So we can reinterpret &x (which is a (T*)[N]) to a U*, and we can do the same with &x + 1. But now those pointers are of type U*, so their difference counts the number of elemenets.
So the array size is:
(U*)(&x + 1) - (U*)(&x)
Note that because of array-to-pointer decay, (U*)(&x) is actually the same pointer as the decay of x.

Resources