简体   繁体   中英

why is array name a pointer to the first element of the array?

总是这样,我的意思是,数组名称总是指向数组的第一个元素的指针。为什么它是这样的呢?它是实现有点事物还是语言特征?

An array name is not itself a pointer, but decays into a pointer to the first element of the array in most contexts. It's that way because the language defines it that way.

From C11 6.3.2.1 Lvalues, arrays, and function designators , paragraph 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.

You can learn more about this topic (and lots about the subtle behaviour involved) from the Arrays and Pointers section of the comp.lang.c FAQ .

Editorial aside: The same kind of behaviour takes place in C++, though the language specifies it a bit differently. For reference, from a C++11 draft I have here, 4.2 Array-to-pointer conversion , paragraph 1:

An lvalue or rvalue of type "array of N T " or "array of unknown bound of T " can be converted to an rvalue of type "pointer to T ". The result is a pointer to the first element of the array.

The historical reason for this behavior can be found here .

C was derived from an earlier language named B (go figure). B was a typeless language, and memory was treated as a linear array of "cells", basically unsigned integers.

In B, when you declared an N-element array, as in

auto a[10];

N cells were allocated for the array, and another cell was set aside to store the address of the first element, which was bound to the variable a . As in C, array indexing was done through pointer arithmetic:

a[j] == *(a+j)

This worked pretty well until Ritchie started adding struct types to C. The example he gives in the paper is a hypothetical file system entry, which is a node id followed by a name:

struct {
  int inumber;
  char name[14];
};

He wanted the contents of the struct type to match the data on the disk; 2 bytes for an integer immediately followed by 14 bytes for the name. There was no good place to stash the pointer to the first element of the array.

So he got rid of it. Instead of setting aside storage for the pointer, he designed the language so that the pointer value would be computed from the array expression itself.

This, incidentally, is why an array expression cannot be the target of an assignment; it's effectively the same thing as writing 3 = 4; - you'd be trying to assign a value to another value.

Carl Norum has given the language-lawyer answer on the question (and got my upvote on it), here comes the implementation detail answer:

To the computer, any object in memory is just a range of bytes, and, as far as memory handling is concerned, uniquely identified by an address to the first byte and a size in bytes. Even when you have an int in memory, its address is nothing more or less than the address of its first byte. The size is almost always implicit: If you pass a pointer to an int , the compiler know its size because it knows that the bytes at that address are to be interpreted as an int . The same goes for structures: their address is the address of their first byte, their size is implicit.

Now, the language designers could have implemented a similar semantic with arrays as they did with structures, but they didn't for a good reason: Copying was then even more inefficient than now compared to just passing a pointer, structures were already passed around using pointers most of the time, and arrays are usually meant to be large. Prohibitively large to force value semantics on them by language.

Thus, arrays were just forced to be memory objects at all times by specifying that the name of an array would be virtually equivalent to a pointer. In order, to not break the similarity of arrays to other memory objects, the size was again said to be implicit (to the language implementation, not the programmer!): The compiler could just forget about the size of an array when it was passed somewhere else and rely on the programmer to know, how many objects were inside the array.

This had the benefit that array accesses are excrutiatingly simple; they decay to a matter of pointer arithmetic, of multiplying the index with the size of the object in the array and adding that offset to the pointer. It's the reason why a[5] is exactly the same as 5[a] , it's a shorthand for *(a + 5) .

Another performance related aspect is that it is excrutiatingly simple to make a subarray from an array: only the start address needs to be calculated. There is nothing that would force us to copy the data into a new array, we just have to remember to use the correct size...

So, yes, it has profound reasons in terms of implementation simplicity and performance that array names decay to pointers the way they do, and we should be glad for it.

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