I have the following code:
int *pa;
int a[3] = {1, 2, 3};
Why pa = a is ok, but a = pa is not allowed?
The main difference is that type of a is still an array but it just decays into a pointer when you do pa=a;. pa will now point to the first element of the array not the entire array itself. When you do a=pa it doesnot make any sense as you are trying point a datatype which is holding 3 integers to a type which can point only to a single integer.
Note: This is purely conceptual, this is not the actual reason why this happens.
I like to think of pointer assignment like OOP & Inheritance.
Imagine int * is a generic object. Now, think of int [] as an object that inherits from int *.
As you can see, you can cast down from int [] to int *, but not casting upwards.
Well, the simple answer is that the language definition simply doesn't allow it — it's a design choice.
6.5.16 Assignment operators
...
Constraints
2 An assignment operator shall have a modifiable lvalue as its left operand.
And what's a modifiable lvalue?
6.3.2.1 Lvalues, arrays, and function designators
1 An lvalue is an expression with an object type or an incomplete type other thanvoid;53) 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.
...
53) The name ‘‘lvalue’’ comes originally from the assignment expressionE1 = E2, in which the left operandE1is 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’’.
Emphasis added.
Array expressions in C are treated differently than most other expressions. The reason for this is explained in an article Dennis Ritchie wrote about the development of the C language:
NB existed so briefly that no full description of it was written. It supplied the typesintandchar, arrays of them, and pointers to them, declared in a style typified by:
int i, j; char c, d; int iarray[10]; int ipointer[]; char carray[10]; char cpointer[];The semantics of arrays remained exactly as in B and BCPL: the declarations of
iarrayandcarraycreate cells dynamically initialized with a value pointing to the first of a sequence of 10 integers and characters, respectively. The declarations foripointerandcpointeromit the size, to assert that no storage should be allocated automatically. Within procedures, the language's interpretation of the pointers was identical to that of the array variables: a pointer declaration created a cell differing from an array declaration only in that the programmer was expected to assign a referent, instead of letting the compiler allocate the space and initialize the cell.Values stored in the cells bound to array and pointer names were the machine addresses, measured in bytes, of the corresponding storage area. Therefore, indirection through a pointer implied no run-time overhead to scale the pointer from word to byte offset. On the other hand, the machine code for array subscripting and pointer arithmetic now depended on the type of the array or the pointer: to compute
iarray[i]oripointer+iimplied scaling the addend i by the size of the object referred to.These semantics represented an easy transition from B, and I experimented with them for some months. Problems became evident when I tried to extend the type notation, especially to add structured (record) types. Structures, it seemed, should map in an intuitive way onto memory in the machine, but in a structure containing an array, there was no good place to stash the pointer containing the base of the array, nor any convenient way to arrange that it be initialized. For example, the directory entries of early Unix systems might be described in C as:
struct { int inumber; char name[14]; };I wanted the structure not merely to characterize an abstract object but also to describe a collection of bits that might be read from a directory. Where could the compiler hide the pointer to
namethat the semantics demanded? Even if structures were thought of more abstractly, and the space for pointers could be hidden somehow, how could I handle the technical problem of properly initializing these pointers when allocating a complicated object, perhaps one that specified structures containing arrays containing structures to arbitrary depth?The solution constituted the crucial jump in the evolutionary chain between typeless BCPL and typed C. It eliminated the materialization of the pointer in storage, and instead caused the creation of the pointer when the array name is mentioned in an expression. The rule, which survives in today's C, is that values of array type are converted, when they appear in expressions, into pointers to the first of the objects making up the array.
This invention enabled most existing B code to continue to work, despite the underlying shift in the language's semantics. The few programs that assigned new values to an array name to adjust its origin—possible in B and BCPL, meaningless in C—were easily repaired. More important, the new language retained a coherent and workable (if unusual) explanation of the semantics of arrays, while opening the way to a more comprehensive type structure.
It's a good article, and well worth reading if you're interested in the "whys" of C.
a = pato do?