简体   繁体   中英

Why can a pointer not be assigned to an array in C?

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.

Chapter and verse :


...


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

And what's a modifiable lvalue?



1 An lvalue is an expression with an object type or an incomplete type other than void ; 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. , 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. ,不具有不完整类型,不具有const限定类型 ,并且如果是结构或联合,则不具有任何成员(包括递归地包括任何成员)或const限定类型的所有包含的集合或联合的元素)。
...
53) 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''.

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 types int and char , arrays of them, and pointers to them, declared in a style typified by
\nint i, j; char c, d; int iarray[10]; int ipointer[]; char carray[10]; char cpointer[];\nint 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 iarray and carray create cells dynamically initialized with a value pointing to the first of a sequence of 10 integers and characters respectively. The declarations for ipointer and cpointer omit 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] or ipointer+i implied 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 name that 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?



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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM