40
char arr[] = "Hello";
arr = arr + 1;  // error occurs

As far as I know, an expression that has array type is converted to pointer type that points to the initial element of the array. Therefore, I expected arr = arr + 1 (pointer to first element(arr) of the array becomes the pointer to the second element of the array)to work. Why doesn't this work in C?

gnat
  • 6,213
  • 108
  • 53
  • 73
Jin
  • 1,902
  • 3
  • 15
  • 26
  • 41
    Because whomever taught you than arrays and pointers are exactly the same, was over-simplifying to the point of being wrong. – StoryTeller - Unslander Monica Aug 09 '16 at 05:02
  • 4
    Some thing similar to http://stackoverflow.com/questions/2351544/is-array-name-a-constant-pointer-in-c – sameerkn Aug 09 '16 at 05:09
  • 9
    Why can't I do `int i; &i = &i + 1;`? `&i` is a pointer, right? – user253751 Aug 09 '16 at 05:12
  • 9
    The **pointer arithmetic** is fine; the problem is the **assignment**. – Pete Becker Aug 09 '16 at 13:35
  • 1
    There is no pointer here, only Array – Michi Aug 09 '16 at 14:14
  • 1
    @Michi: Check again, it's very subtle, but there is a pointer involved, and the pointer is the cause of the OP's error (though the OP described the error incorrectly) – Mooing Duck Aug 09 '16 at 19:21
  • 2
    @MooingDuck I do realize what happens on the right side, but this is not the OP's problem. The problem is left side. Arrays are not assignable. So there is no reason to speak about pointers here as long as the problem is the Array itself. Even if the Array name is a Pointer this doesn't mean that one should speak about this part. The Answers below covers all this. – Michi Aug 09 '16 at 20:37
  • 1
    @PeterMortensen Imagine yourself that this Question got +33 :)) – Michi Aug 09 '16 at 22:44

6 Answers6

56

arr + 1 is indeed a pointer to the second element of the array (i.e. &arr[1]).

However, that does not mean that you can somehow write that pointer value back into arr. You can't do it for at least two reasons.

Firstly, arr is an array of char elements, not a pointer. There's an obvious type mismatch here.

Secondly, being an array, arr a non-modifiable lvalue. You cannot change arr itself, you can only change its elements (this distinction is somewhat hard to grasp, but it is there).

Finally, if we just ignore the deeper intricacies and focus on what formally happens at the top level, due to array type decay your expression is equivalent to

(char *) arr = (char *) arr + 1;

The assignment is impossible since the left-hand side is a result of [implicit] type conversion. In C type conversions always produce rvalues. You cannot assign to rvalues.

In other words, it is not the "pointer arithmetic" that's disallowed here. The pointer arithmetic is fine. It is what you do with the result of that pointer arithmetic that causes the error.

Community
  • 1
  • 1
AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • So here can we say `arr` is a constant array variable ? – GShaik Aug 09 '16 at 11:44
  • 3
    @AbdulGafoor: best not to, because saying "constant array variable" in the context of C sounds like you mean it's a `const char [6]`, whereas actually it's a `char [6]`. It's not the array that's constant, it's that array variables of any type cannot be assigned to. – Steve Jessop Aug 09 '16 at 11:53
  • 2
    @AbdulGafoor You can say `arr` is a "non-modifiable l-value". The only way to get an effect like you desire is e.g. `char arr[] = "Hello"; char *ptr = arr; ptr = ptr + 1;` and work with `ptr`. – Jason C Aug 09 '16 at 13:07
  • 1
    @AbdulGafoor The only thing you should know is that arr is an Array not a Pointer and the fact that Arrays are not assignable – Michi Aug 09 '16 at 14:21
  • So modifying `arr` is l-value error. – GShaik Aug 09 '16 at 17:19
  • @AbdulGafoor In that context yes. – Michi Aug 09 '16 at 20:41
  • I disagree with the equivalence of `arr = arr + 1;` with `(char *) arr = (char *) arr + 1;`. `arr` on left side of `=` has type `char [6]` at the time of assignment and that's make it non-modifiable. – haccks Aug 09 '16 at 21:30
  • @haccks: I don't see why you disagree. The language spec explicitly states in 6.3.2.1 that array type *always* decays to pointer type in *all* contexts except for a few chosen ones. And left-hand side of assignment operator is not one of those chosen ones. So, by the time we get to the actual assignment, the left-hand size is already a rvalue pointer, not an array. – AnT stands with Russia Aug 09 '16 at 22:21
  • @AnT; At the time of assignment its type is of array type otherwise it would be modifiable lvalue. – haccks Aug 09 '16 at 22:23
  • @haccks: Again, I don't see where you are getting this from. What exactly are you referring to? Why would it be "a modifiable lvalue"? Arrays are *never* modifiable lvalues, regardless of anything else. – AnT stands with Russia Aug 09 '16 at 22:26
  • @AnT; Yes, arrays are never modifiable lvalues that's because on left of `=` its type remain array type at the time of assignment. Array decay to pointer on left of `=` operator when it's value is fetched but once comes the assignment part its type is array type and that array type makes it non-modifiable because compiler know by then that it is an array not a pointer. – haccks Aug 09 '16 at 22:34
  • @haccks: Well, you made a lot of assertive statements: "on left of `=` its type remain array type at the time of assignment", "once comes the assignment part its type is array type". But honestly, with all due respect, I don't believe these statements have any basis in the reality of standard C. Can you back any of that with references to the standard? – AnT stands with Russia Aug 09 '16 at 22:39
  • Well, if I had any backup from the standard then I already have given. I said I am disagree with the equivalence you said. I never said you are wrong. I gave my assertion that how it should work. There is difference between rvalue and non modifiable lvalue. If left hand side become rvalue then compiler should raise an error about rvalue instead of non modifiable lvalue. BTW, I might be thinking in a wrong direction or missing something here so please don't mind. – haccks Aug 09 '16 at 22:52
23

Arrays are non-modifiable lvalues. They can't be the left operand of an assignment operator.

C11-§6.3.2.1:

A modifiable lvalue is an lvalue that does not have array type, does not have an incomplete type, [...]

§6.5.16/2:

An assignment operator shall have a modifiable lvalue as its left operand.

In the statement

arr = arr + 1; 

arr is the left operand of = operator and is of array type. It can't be modified.
So, it's not the pointer arithmetic but the constraint by the language on the assignment operator that is the reason for syntactical error.


Note that in some contexts arrays decay to a pointer to its first element, though pointers and arrays are different types. Arrays are not pointers. It is only the pointer arithmetic and array indexing which are equivalent. For example

char *ptr = &arr[0] + 1 => &(*(arr + 0)) + 1 => &(*arr) + 1 => arr + 1 // * and & nullify each other 
haccks
  • 104,019
  • 25
  • 176
  • 264
  • 3
    You may even call it "array indexing *syntax*": it decays the array to a pointer, then indexes that. – Quentin Aug 09 '16 at 17:23
10

This is because arrays are similar to pointers except that they can not be modified. However you can modify a pointer that is pointing to an array. For the above example you can do like this:

char arr[]="Hello";
char *ptr=arr;
ptr=ptr+1;

Initially the pointer ptr will be pointing to the first character of the array i.e. 'H' and after modifying the value it will point to the second character i.e. 'e'. You can also do the following:

char arr[]="Hello";
char *ptr=arr;
ptr=arr+1;

Both produce the same effect which shows that arr+1 is indeed pointer arithmetic. However you can not modify the value of arr because its type is that of a character array and not pointer to a character array.

Nisse Engström
  • 4,738
  • 23
  • 27
  • 42
Natesh Raina
  • 177
  • 9
8

As far as I know, an expression that has array type is converted to pointer type that points to the inital element of the array.

It is true in most contexts. It is not true in the following contexts:

  1. When using the addressof operator (&arr). The type of &arr is char (*)[6]. It is not char**.

  2. When using the sizeof operator. sizeof(arr) is 6. Had it been a pointer, it would be the size of a pointer (4 or 8 in most common platforms).

  3. When used as the LHS of an assignment operator. A variable of array type is not modifiable.

Therefore, I expected arr = arr + 1 (pointer to first element(arr) of the array becomes the pointer to the second element of the array)to work. Why doens't this work in C?

The RHS of the expression evaluates to a pointer to the second element of arr. However, that line does not work due to (3) above. arr is not a modifiable value. It cannot be used as the LHS of an assignment operator.

R Sahu
  • 204,454
  • 14
  • 159
  • 270
4

char[] is not pointer, while char* is a pointer. This works, but it is wrong solution:

int main()
{
    char *arr = "Hello";
    arr = arr + 1;  // Wrong!
    printf("%s\n", arr); // Output: ello
}

If arr is heap-allocated with malloc you can get memory leak if free memory starting arr+1 not arr.

But you can do something like this:

int main()
{
    char arr[] = "Hello";
    char *wordFromSecondLetter = &arr[0] + 1;
    printf("%s\n", wordFromSecondLetter); // Output: ello
}

Or like this

int main()
{
    char arr[] = "Hello";
    printf("%s\n", &arr[1]); // Output: ello
}
Inline
  • 2,566
  • 1
  • 16
  • 32
3

Because arr is not a pointer but a char array. You can verify this by checking sizeof arr. To get a pointer to char, you should use char *arr = "Hello";.

The biggest difference between a pointer and an array is that you can directly assign a value to a pointer, but you can't do this to an array.

In fact, when you write arr + 1, arr "decays" to the pointer to its first element, that is to say, arr == &arr[0]. So arr + 1 is legal pointer arithmetic, but giving its value to arr, which is an array, is illegal.

nalzok
  • 14,965
  • 21
  • 72
  • 139