Widd
Widd

Reputation: 21

in C, how come the address of array, &array, &array[0] are the same?

int main(){
        long a[]={6,9,10};

        printf("%p\n",a);
        printf("%p\n",&a);
        printf("%p\n",&a[0]);

return 0;
}

the result is:

0x7ff9c94a10
0x7ff9c94a10
0x7ff9c94a10

this result really baffles me a lot! how come it stores different values at the same memory locaton, at 0x7ff9c94a10 exists there the address itself and value 6 simultaneously

Upvotes: 2

Views: 1374

Answers (3)

Luis Colorado
Luis Colorado

Reputation: 12668

Well,

array

is, by definition, the address of the first element, this is the same as:

&array[0]

so this explains why both have the same address.

The second expression is the address of the array. Normally, (I don't remember at this moment if that is enforced by the standard or not) an array is stored by putting its elements one after the other, starting on the first one... so it is normal that the array address is the same as the address of the first element. But if you check the types of the pointed to objects, you will see the differences between these expressions: The type of

*array

is the type of an element of the array, while the type of

*&array

is the whole array type.

So, let's assume you have

char array[10];

then

array

is of type

char *

and

*array

is of type char, while

&array

is of type array of 10 chars, or

char [10]

How to verify this? Just run the following program:

#include <stdio.h>

/* print the expression and it's size */
#define P(T) printf("sizeof("#T") == %zu\n", sizeof(T))

int main()
{
    char array[10];

    P(   array    );
    P(  *array    );
    P(  &array    );
    P( *&array    );
    P(   array[0] );
    P(  &array[0] );
    P( *&array[0] );
}

When run (on my system) it produces:

$ a.out
sizeof(array) == 10         <-- the size of the array
sizeof(*array) == 1         <-- the size of the pointed to first element (as by def)
sizeof(&array) == 8         <-- size of a pointer to the array.
sizeof(*&array) == 10       <-- size of the pointed to object (the array)
sizeof(array[0]) == 1       <-- size of the first element.
sizeof(&array[0]) == 8      <-- size of a pointer to the first element (as above)
sizeof(*&array[0]) == 1     <-- size of the pointed to first element.
$ _

(the texts on the right are the explanations on the numbers)

The reason of the error on && (this belongs to one of the answers given, that for some reason has chained the & into two applications of it and got an error about a label operator) is that GCC has an unary operator && for a GCC extension to the language. There's no && unary operator in C, and you'd get an error if you resume to -pedantic, about a syntax error when trying to use the logical and operator (&&) without a left operand. Anyway, if you tried to separate the operands as in

& & array

then you would get another error because the subexpression &array doesn't denote an lvalue (has no representation in memory) and so, cannot be applied the & left unary operator (you cannot get an address of a value).

Your last paragraph is interesting: You say

this result really baffles me a lot! how come it stores different values at the same memory locaton, at 0x7ff9c94a10 exists there the address itself and value 6 simultaneously

they are not different values. The problem here is that two of the three different expressions represent different things, but in the same place, because the array starts in memory at the same point as it's first element.

When you are at home, you, and your house have the same postal address, but you and your house are different things, right? This is exactly what is happening here: &array (is the address of your house, the array) and &array[0] (is your address, which is the same as the postal addres of the house because you are in the house). The problem is that you have three expressions and don't get that the expression array means, by how it was defined, literally A POINTER TO THE FIRST ELEMENT and this makes it equivalent to &array[0] (like an abreviation). So we end having three expressions with the same value, two of them being the same thing (exactly) and the other meaning something different. Probably after you read this paragraph, you'll need to read again the output of the simple program above and you'll see where are the differences (obviously, your house is bigger than you)

Upvotes: 1

user15741790
user15741790

Reputation:

a is a synonym (by definition) for &a[0] as K&R gently explain in "Arrays and Pointers" chapter. &a is not mentioned, but can be regarded as intermediate step.

You have to concentrate on the identifier, here a. All the compiler needs is the address where a begins and what type its elements are. The 2nd element is bound to be at the base address plus one element size.

&a and &a[0] are rather expressions to evaluate than values to store. Also &a[23] is calculated, not stored: a + 23 * elemsize.

    printf("%p\n",   a     );  

    printf("%p\n",   a+1   );  // address of next element, not byte  
    printf("%p\n", &(a[1]) );  // parens not needed

The 2nd and 3rd line print an address 8 bytes (long size) above the array start.


at 0x7ff9c94a10 exists there the address itself

Maybe that address really exists at the same spot. like we all somehow do. But it holds a long "6".

To fill an array with its addresses you can:

    void *a[3];
    for (int i = 0; i < 3; i++)
        a[i] = &a[i];


    for (int i = 0; i < 3; i++) {
        printf("%p\n",  a[i]);
        printf("%p\n", &a[i]);
    }

prints:

0x7ffe0918cbe0
0x7ffe0918cbe0
0x7ffe0918cbe8
0x7ffe0918cbe8
0x7ffe0918cbf0
0x7ffe0918cbf0

It takes 8 bytes to store an address/pointer. long and void * have same size here.


printf("%p\n", a+i); also works. No need to take reference of a derefernced element: &a[i].

One basic array-and-pointer rule is the E1[E2] is *(E1 + E2). E1 has to designate an array, and E2 and integer, so

a[i]
*(a + i)

are the same. As are

&(a[i])
&a[i]
a + 1

The idea of an array starting at a defined address is simple. But to talk about it...natural language can live with double senses:

"Have you seen number 12 today?" (the guest)

"Did you clean number 12?" (not the guest, the room)

bafflement

Somehow. But while after void **b = a; you can use the pointer b just like a (only that it has its own new &b) a = ... is not possible (assignment to the naked array).

New Example

Your long type was good for really storing pointers to something, but it made the above example complex. Here a simple int array, so nobody gets tempted to store addresses. It shows what an array and a pointer share and what not:

int main(){
        int a[3] = {10,20,30};

        int *b;
        b = a;

        for (int i = 0; i < 3; i++) {
            printf("%p\n",  &a[i]);
            printf("%p\n",  &b[i]);
            printf("%p %d\n",   a+i, a[i]);
            printf("%p %d\n",   b+i, b[i]);
        }
        printf("%p\n",        a);
        printf("%p b = a\n",  b);
        printf("%p\n",                          &a);
        printf("%p <-- different, &b != &a\n",  &b);

return 0;
}

This gives:

0x7fff72f0ed8c
0x7fff72f0ed8c
0x7fff72f0ed8c 10
0x7fff72f0ed8c 10
0x7fff72f0ed90
0x7fff72f0ed90
0x7fff72f0ed90 20
0x7fff72f0ed90 20
0x7fff72f0ed94
0x7fff72f0ed94
0x7fff72f0ed94 30
0x7fff72f0ed94 30
0x7fff72f0ed8c
0x7fff72f0ed8c b = a   (same as top index zero)
0x7fff72f0ed8c
0x7fff72f0ed80 <-- different, &b != &a

The difference (12 bytes, "0" for a "c") is not dramatic, but that does not matter. b has a life besides the array elements, a not.

Along with that goes the fact that the pointer b can be assigned to (as done with b = a), but the array a not.

How come ?

the addresses of array, &array, &array[0]

This is actually very unclear. The address of array is &array (somehow), but the address of &array is neither &&array (errror about a label 'array') nor &(&array) (error: lvalue required).

Upvotes: 0

John Bode
John Bode

Reputation: 123458

A picture should help - we’ll assume a 64-bit long:

             +––––––+
0x7ff9c94a10 | 0x06 | a[0]
             +––––––+
0x7ff9c94a18 | 0x09 | a[1]
             +–––—––+
0x7ff9c94a20 | 0x0a | a[2]
             +–––––—+

There is no object a separate from the array elements themselves, so the address of the array (&a, type long (*)[3]) is the same as the address of its first element (&a[0], type long *).

Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted, or "decay", to an expression of type "pointer to T" and the value of the expression is the address of the first element of the array, so the expression a is effectively the same as &a[0] and also has type long *.

Upvotes: 1

Related Questions