How come an array's address is equal to its value in C?
Asked Answered
C

6

235

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 = 0022FF00
&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;
}
Conception answered 27/3, 2010 at 5:59 Comment(3)
From the comp.lang.c FAQ: - [So what is meant by the ``equivalence of pointers and arrays'' in C? ](c-faq.com/aryptr/aryptrequiv.html) - [Since array references decay into pointers, if arr is an array, what's the difference between arr and &arr? ](c-faq.com/aryptr/aryvsadr.html) Or go read the entire Arrays and Pointers section.Drupe
I had added an answer with diagram to this question two years back here What does sizeof(&array) return?Cheongsam
Does this answer your question? What is array to pointer decay?Vertievertiginous
C
254

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
Colver answered 27/3, 2010 at 6:4 Comment(14)
Yep, adding 1 to each leads to different results. Do you know what type each belongs to?Conception
@Alexandre: &array is a pointer to the first element of the array, where as array refers to the entire array. The fundamental difference can also be observed by comparing sizeof(array), to sizeof(&array). Note however that if you pass array as an argument to a function, only &array is in fact passed. You cannot pass an array by value unless it is encapsulated withing a struct.Bumpy
@Clifford: If you pass array to a function, it decays to a pointer to its first element so effectively &array[0] is passed, not &array which would be a pointer to the array. It may be a nit-pick but I think it's important to make clear; compilers will warn if the function has a prototype that matches the type of the pointer passed in.Damsel
@Charles Bailey: Fair enough, if somewhat pedantic since the address of the array and the address of the first element of an array are the same address even if semantically different.Bumpy
@Jerry Coffin For example int *p = &a, if I want the memory address of the int pointer p, I can do &p. Since &array converts to the address of the whole array (which starts at the address of the first element). Then how do I find the memory address of the array pointer (which stores the address of the first element in the array)? It must be somewhere in the memory right?Spud
@JohnLee: No, there does not have to be a pointer to the array anywhere in memory. If you create a pointer, you can then take its address: int *p = array; int **pp = &p;.Colver
@JerryCoffin Thanks. I read in a C book. It says "When you create a pointer variable, the machine will allocate 4 or 8 bytes of space to store it. But what if you create an array? The computer will allocate space to store the array, but it won’t allocate any memory to store the array variable. The compiler simply plugs in the address of the start of the array."Spud
@JohnLee: That quotation doesn't make much sense. Do you know what "it won't allocate any memory to store the array variable" means? Perhaps the word "variable" is being used in some odd way.Lanceolate
I think this answer fails to explain why only in the context of static allocated arrays does variable == &variableEmulous
@DanF: There's no such thing to explain -- given a local array, variable and &variable will give the same address.Colver
@JerryCoffin FYI, There are two more cases when an array doesn't decay into a pointer: when it's the operand of C11's alignof operator, and when it's a string literal (char []) which is used to initialize a char array. (Also, in C++, another exception is, AFAIK, when an array is passed by reference, but we are not talking about C++ here anyway.)Undulant
Why &(array+1) results in error "lvalue required as unary '&' operand". array+1 is not a literal. So confused with this behavior.Intolerable
@Bumpy the first comment is wrong, why still keep it? I think it might lead to misunderstanding for those who don't read the following (@Charles) reply.Cerallua
The name of an array usually evaluates to the address of the first element of the array, AND so array and &array have the same value - These two parts are not relatedPicador
T
39

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.

Tetryl answered 27/3, 2010 at 6:2 Comment(8)
Shouldn't &my_array be an invalid operation since my_array's value isn't on the stack, only my_array[0...length] are? Then it would all make sense...Conception
@Alexandre: I'm not sure why it's allowed, actually.Tetryl
You can take the address of any variable (if not marked register) whatever its storage duration: static, dynamic or automatic.Damsel
my_array itself is on the stack, because my_array is the entire array.Almira
@caf: The array is definitely on the stack, but my_array yields the memory address of the first element. Where is that memory address stored? If it was a pointer (char *) it would be stored on the stack, but in the case of my_array, where is the memory address stored? If the stack has |000|001|002|...|099| , where each is a slot in memory corresponding to the int value of the array's elements, does the compiler reserve another slot to indicate exactly where the first element is in memory?Conception
my_array, when not the subject of the & or sizeof operators, is evaluated to a pointer to its first element (ie. &my_array[0]) - but my_array itself is not that pointer (my_array is still the array). That pointer is just an ephemeral rvalue (eg. given int a;, it is just like a + 1) - conceptually at least it is "calculated as needed". The real "value" of my_array is the contents of the entire array - it is just that pinning down this value in C is like trying to catch fog in a jar.Almira
my_array is the name of an array. It's not "an alias to the address of an array". Since my_array designates an array, it's tautological to say that &my_array is defined as the address of the array. This is just the same as how with int x; , &x is defined as the address of the int. The array is a stack variable (in this case - of course, you can make them static and so on), so you can take its address in just the same way that you can take an int's address.Tautomer
Thx for the detailed blog-post. You missed one thing though: I can imagine a perfectly fine use for pointers to array. It is to encode in the type-system the size of array. I'd say it's mostly for human readers, but there's more to it: if you try passing a pointer to an array of different size, the compiler gonna warn you like expected ‘int (*)[5]’ but argument is of type ‘int (*)[4]’Devland
D
34

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.

Damsel answered 27/3, 2010 at 10:57 Comment(0)
H
6

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].
Hamner answered 17/2, 2015 at 22:10 Comment(0)
L
5

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.

Lambertson answered 14/9, 2015 at 17:43 Comment(0)
F
2

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);
Feedback answered 28/6, 2013 at 9:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.