Why can't I assign an array to another array in C

Viewed 13062
#include<stdio.h>    

int main(){    
  int a[] = {1,2,3};
  int b[] = {4,5,6};
  b = a;
  return 0;  
 } 

Result in this error:

array type 'int [3]' is not assignable

I know arrays are lvalues and are not assignable but in this case, all the compiler has to do is reassign a pointer. b should just point to the address of a. Why isn't this doable?

5 Answers

"I know arrays are lvalues and are not assignable but in this case, all the compiler has to do is reassign a pointer."

"b should just point to the address of a. Why isn't this doable?"

You seem to confuse here something. b isn't a pointer. It is an array of three int elements.

b = a;

Since b is used here as lvalue in the assignment, it is taken as of type int [3], not int *. The pointer to decay rule takes no place in here for b, only for a as rvalue.

You cannot assign an array (here b) by a pointer to the first element of another array (here a) by using b = a; in C.

The syntax doesn't allow that.

That's what the error

"array type 'int [3]' is not assignable"

is saying to you for b.

Also you seem to be under the misunderstanding that the pointer to decay rule means that an array is anyhow converted to a pointer object, which can in any manner store addresses of locations of different objects.

This is not true. This conversion is only happening in a very implicit kind of way and is subject of this SO question:

Is the array to pointer decay changed to a pointer object?


If you want to assign the values from array a to the array b, you can use memcpy():

memcpy(b, a, sizeof(a));

I know arrays are lvalues and are not assignable but in this case, all the compiler has to do is reassign a pointer. b should just point to the address of a. Why isn't this doable?

Because b isn't a pointer. When you declare and allocate a and b, this is what you get:

+---+
| 1 | a[0]
+---+
| 2 | a[1]
+---+
| 3 | a[2]
+---+
 ...
+---+
| 4 | b[0]
+---+
| 5 | b[1]
+---+
| 6 | b[2]
+---+

No space is set aside for any pointers. There is no pointer object a or b separate from the array elements themselves.

C was derived from an earlier language called B, and in B there was a separate pointer to the first element:

   +---+
b: | +-+--+
   +---+  |
    ...   |
     +----+
     |
     V
   +---+
   |   | b[0]
   +---+
   |   | b[1]
   +---+
    ...
   +---+
   |   | b[N-1]
   +---+

When Dennis Ritchie was developing C, he wanted to keep B's array semantics (specifically, a[i] == *(a + i)), but he didn't want to store that separate pointer anywhere. So instead he created the following rule - unless it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T" and the value of the expression will be the address of the first element of the array, and that value is not an lvalue.

This has several practical effects, the most relevant here being that an array expression may not be the target of an assignment. Array expressions lose their "array-ness" under most circumstances, and simply are not treated like other types.

Edit

Actually, that misstates the case - array expression may not be the target of an assignment because an array expression is not a modifiable lvalue. The decay rule doesn't come into play. But the statement "arrays are not treated like other types" still holds.

End Edit

The upshot is that you cannot copy the contents of one array to the other using just the = operator. You must either use a library function like memcpy or copy each element individually.

Others already explained what you got wrong. I'm writing that answer to explain that actually the compiler could assign an array to another, and you can achieve the same effect with minimal change to your sample code.

Just wrap your array in a structure.

#include <stdio.h>

int main(){
  struct Array3 {
    int t[3];
  };
  struct Array3 a = {{1,2,3}};
  struct Array3 b = {{4,5,6}};
  a = b;
  printf("%d %d %d", a.t[0], a.t[1], a.t[2]);
  return 0;
 }

Once the array is wrapped in a structure copying the array member of the structure works exactly as copying any other member. In other words you are copying an array. This trick is usefull in some cases like when you really want to pass an array to a function by copying it. It's slightly cleaner and safer than using memcopy for that purpose, which obviously would also work.

Henceforth the reason why it is not allowed for top level arrays is not because the compiler can't do it, but merely because that's not what most programmers usually wants to do.

Usually they just want to decay the array to a pointer. Obviously that is what you thought it should do, and direct copy of array is likely forbiden to avoid specifically that misunderstanding.

From The C Programming Language:

The array name is the address of the zeroth element.

There is one difference between an array name and a pointer that must be kept in mind. A pointer is a variable. But an array name is not a variable.

My understanding is that the array name is a constant, so it can't be assigned.

The variable b in your code is allocated on the stack as 3 consecutive ints. You can take the address of b and store it in a variable of type int*. You could assign a value to it if you allocate the array on the heap and store only the pointer to it on the stack, in this case you could, in fact, be able to change the value of the pointer to be the same as a.

Related