How come an array's address is equal to its value in C?
In the following bit of code, pointer values and pointer addresses differ as expected.
But array values and addresses don't!
How can this be?
Output
my_array = 0022FF开发者_JAVA百科00
&my_array = 0022FF00
pointer_to_array = 0022FF00
&pointer_to_array = 0022FEFC
#include <stdio.h>
int main()
{
char my_array[100] = "some cool string";
printf("my_array = %p\n", my_array);
printf("&my_array = %p\n", &my_array);
char *pointer_to_array = my_array;
printf("pointer_to_array = %p\n", pointer_to_array);
printf("&pointer_to_array = %p\n", &pointer_to_array);
printf("Press ENTER to continue...\n");
getchar();
return 0;
}
The name of an array usually evaluates to the address of the first element of the array, so array
and &array
have the same value (but different types, so array+1
and &array+1
will not be equal if the array is more than 1 element long).
There are two exceptions to this: when the array name is an operand of sizeof
or unary &
(address-of), the name refers to the array object itself. Thus sizeof array
gives you the size in bytes of the entire array, not the size of a pointer.
For an array defined as T array[size]
, it will have type T *
. When/if you increment it, you get to the next element in the array.
&array
evaluates to the same address, but given the same definition, it creates a pointer of the type T(*)[size]
-- i.e., it's a pointer to an array, not to a single element. If you increment this pointer, it'll add the size of the entire array, not the size of a single element. For example, with code like this:
char array[16];
printf("%p\t%p", (void*)&array, (void*)(&array+1));
We can expect the second pointer to be 16 greater than the first (because it's an array of 16 char's). Since %p typically converts pointers in hexadecimal, it might look something like:
0x12341000 0x12341010
That's because the array name (my_array
) is different from a pointer to array. It is an alias to the address of an array, and its address is defined as the address of the array itself.
The pointer is a normal C variable on the stack, however. Thus, you can take its address and get a different value from the address it holds inside.
I wrote about this topic here - please take a look.
In C, when you use the name of an array in an expression (including passing it to a function), unless it is the operand of the address-of (&
) operator or the sizeof
operator, it decays to a pointer to its first element.
That is, in most contexts array
is equivalent to &array[0]
in both type and value.
In your example, my_array
has type char[100]
which decays to a char*
when you pass it to printf.
&my_array
has type char (*)[100]
(pointer to array of 100 char
). As it is the operand to &
, this is one of the cases that my_array
doesn't immediately decay to a pointer to its first element.
The pointer to the array has the same address value as a pointer to the first element of the array as an array object is just a contiguous sequence of its elements, but a pointer to an array has a different type to a pointer to an element of that array. This is important when you do pointer arithmetic on the two types of pointer.
pointer_to_array
has type char *
- initialized to point at the first element of the array as that is what my_array
decays to in the initializer expression - and &pointer_to_array
has type char **
(pointer to a pointer to a char
).
Of these: my_array
(after decay to char*
), &my_array
and pointer_to_array
all point directly at either the array or the first element of the array and so have the same address value.
The reason why my_array
and &my_array
result in the same address can be easily understood when you look at the memory layout of an array.
Let's say you have an array of 10 characters (instead the 100 in your code).
char my_array[10];
Memory for my_array
looks something like:
+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array.
In C/C++, an array decays to the pointer to the first element in an expression such as
printf("my_array = %p\n", my_array);
If you examine where the first element of the array lies you will see that its address is the same as the address of the array:
my_array[0]
|
v
+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+
^
|
Address of my_array[0].
In the B programming language, which was the immediate predecessor to C, pointers and integers were freely interchangeable. The system would behave as though all of memory was a giant array. Each variable name had either a global or stack-relative address associated with it, for each variable name the only things the compiler had to keep track of was whether it was a global or local variable, and its address relative to the first global or local variable.
Given a global declaration like i;
[there was no need to specify a type, since everything was an integer/pointer] would be processed by the
compiler as: address_of_i = next_global++; memory[address_of_i] = 0;
and a statement like i++
would be processed as: memory[address_of_i] = memory[address_of_i]+1;
.
A declaration like arr[10];
would be processed as address_of_arr = next_global; memory[next_global] = next_global; next_global += 10;
. Note that as soon as that declaration was processed, the compiler could immediately forget about arr
being an array. A statement like arr[i]=6;
would be processed as memory[memory[address_of_a] + memory[address_of_i]] = 6;
. The compiler wouldn't care whether arr
represented an array and i
an integer, or vice versa. Indeed, it wouldn't care if they were both arrays or both integers; it would perfectly happily generate the code as described, without regard for whether the resulting behavior would likely be useful.
One of the goals of the C programming language was to be largely compatible with B. In B, the name of an array [called a "vector" in the terminology of B] identified a variable holding a pointer which was initially assigned to point to to the first element of an allocation of the given size, so if that name appeared in the argument list for a function, the function would receive a pointer to the vector. Even though C added "real" array types, whose name was rigidly associated with the address of the allocation rather than a pointer variable that would initially point to the allocation, having arrays decompose to pointers made code which declared a C-type array behave identically to B code which declared a vector and then never modified the variable holding its address.
Actually &myarray
and myarray
both are the base address.
If you want to see the difference instead of using
printf("my_array = %p\n", my_array);
printf("my_array = %p\n", &my_array);
use
printf("my_array = %s\n", my_array);
printf("my_array = %p\n", my_array);
精彩评论