Why Is Arr and &Arr the Same

Why is arr and &arr the same?

They're not the same. They just are at the same memory location. For example, you can write arr+2 to get the address of arr[2], but not (&arr)+2 to do the same.

Also, sizeof arr and sizeof &arr are different.

If arr[3] is an array and ptr is a pointer, then why do arr and &arr give same result but ptr and &ptr don't

Arrays are not pointers!

Arrays do decay to a pointer to their first element in all sorts of circumstances. For example std::cout << arr; actually prints the memory address of the first element of the array. std::cout << &arr; prints the memory address of the array. As the address of the first element is the same as the address of the array you see the same value.

However, just because they have the same value, does not mean they are the same. arr can decay to a int*, while &arr is a pointer to an array, a int(*)[3].

I hope the following will help to clear things up a little:

#include <iostream>
#include <type_traits>

void make_it_decay(int x[]) {
std::cout << std::is_same_v< decltype(x), int*> << "\n";
}

int main() {
int arr[3] = {1,2,3};

//std::cout << (arr == &arr) << "\n"; // does not compile !
std::cout << (arr == &(arr[0])) << "\n";

std::cout << std::is_same_v< decltype(arr), int[3]> << "\n";
std::cout << std::is_same_v< decltype(&arr),int(*)[3]> << "\n";
std::cout << std::is_same_v< decltype(&arr[0]), int* > << "\n";
make_it_decay(arr);
}

output:

1
1
1
1
1

I use decltype to infer the type of certain expressions and std::is_same_v to see if the expressions are of same type.

arr is of type int[3]. It is an array. It is not a pointer.

&arr is the address of the array. It is a pointer to an array with three elements, a int(*)[3].

&arr[0] even though it has the same value as &arr is of different type. It is a pointer to int, an int*.

When we pass arr to a function then it decays to a pointer to the first element of the array. And we can see that inside the function x is int*.


Now to your quesiton...

Above I tried to lay out what happens when you write std::cout << arr. Pointers are different, because ... well arrays are not pointers.

std::cout << ptr;  // prints the value ptr
std::cout << &ptr; // prints the address of ptr

Perhaps some visualization helps. The difference in types gets most apparent when incrementing the pointers

 -------------------
| arr |
-------------------
| 1 | 2 | 3 |
-------------------
^ ^ ^
&arr | &arr + 1
&arr[0] |
&arr[0] + 1

Why *(&arr+4) is different then &arr[4]?

arr is an array:

arr: int[6]
[0|1|2|3|4|5]

When used in an expression other than sizeof arr or &arr, an array decays to a pointer to the first element of the array:

(arr+0): int *
[*]
|
v
[0|1|2|3|4|5]

When an integral value is added to a pointer, you get a pointer to the address that is (sizeof (T)) * n bytes later in memory (where T is the type pointed to by the pointer, and n is the integral value added to the pointer):

(arr+4): int *
[*]
|
v
[0|1|2|3|4|5]

When a pointer is dereferenced, you get the value that was pointed to:

*(arr+4): int
4 /* specifically, the 4 in the fifth position in `arr` */
[0|1|2|3|[4]|5]

When the address is taken of an int, you get a pointer that points to that int:

&*(arr+4): int *
[*] /* notice, this is the same as (arr+4) */
|
v
[0|1|2|3|4|5]

Array indexing is equivalent to pointer-addition followed by dereferencing:

arr[4] == *(arr+4) /* see above for definition of *(arr+4) */

So yes... &*(arr+4) and &arr[4] are equivalent.

When the address of an array is taken, you get a pointer to an array:

&arr: int (*)[6]
[*] /* points to the array as a whole, not the first element of the array */
|
v
[0|1|2|3|4|5]

When you increment that pointer, the same rules apply as above:

                                    &arr + 4: int(*)[6]
/*points into some memory that*/ [*]
/* isn't part of the array... */ |
/* undefined behaviour */ v
[0|1|2|3|4|5][x|x|x|x|x|x][x|x|x|x|x|x][x|x|x|x|x|x][x|x|x|x|x|x]

Since this has undefined behaviour, you can't reason about it without reference to the underlying machine architecture and compiler implementation.

If we imagine that it were well defined (as would be the case if arr were part of a larger array)... we can continue. Dereferencing a pointer to an array gives the array again:

             /*the 5th array in this array of arrays*/ *(&arr+4): int[6]
[0|1|2|3|4|5][x|x|x|x|x|x][x|x|x|x|x|x][x|x|x|x|x|x][[x|x|x|x|x|x]]

You would find that *(&arr+4) and (&arr+4) have the same address, since *(&arr+4) decays into a pointer to the first element of *(&arr+4), and an array starts at its first element, so a pointer to the start of an array and a pointer to the first element of an array would be identical.

*(&arr+4) is different from &arr[4] since it refers to a completely different thing (see above).

Is arr, *arr, arr[0] and &arr[0][0] the same in C?

In C an array under most situations can decay to a pointer to the first element of the array. So:

  • B is of type double[3][3] an decays to double (*)[3] (pointer to array of 3 doubles)

  • *B and B[0] are identical and are of type double[3] (array of 3 doubles) and decay to double * (pointer to double).

  • &B[0][0] is of type double *.

And then there are function parameters: In C arrays cannot be function parameters. So when used as the type of a function parameter what looks like an array is not an array, but it is in fact a pointer, ignoring the first array dimension, e.g.:

void foo(double arr[3])
void foo(double arr[])

Are in reality:

void foo(double* arr)

The same is true for multidimensional arrays:

void foo(double arr[2][3])
void foo(double arr[][3])

Are in reality:

void foo(double (*arr)[3])

With the above in mind let's go through your examples:

void foo(double p[][3]);
// is actually:
void foo(double (*p)[3]);

foo(A);

A decays to double (*)[3] which is the expected parameter type. So everything is ok.

void foo(double* p);

foo(&B[0][0])

No problems here either, you directly pass a pointer to the first double element.

void foo(double* p)

foo(C[0]);

C[0] is of type double[3] and decays to double* which is the expected parameter type. Again, ok.

In an array, what does &arr[2] return?

Let's look at a simple example first:

int a;
a = 5;

In a sense the integer a has two values assoicated with it. The one you most
likely think about first is the rvalue, which in this case is the number 5.
There is also what is called an lvalue (pronounced "el value") which is the
memory address the integer a is located at.

This is an important concept to grasp. At the end of the day everything is all
about memory. We store code and variables in memory. The CPU executes
instructions which are located in memory and it performs actions on data which
is also in memory. It's all just memory. Nothing very complicated; if someone
tries to scare you with pointers don't listen, it's all just memory :)

Alrighty so, in the case of an array we are dealing with a contiguious block of
memory that is used for storing data of the same type:

int array[] = {0, 1, 1, 2, 3, 5, 8, 13, 21};

As you have already noted the name of the array refers to the memory location of
the first element in the array (e.g. array == &array[0]). So in my example array
above &array[2] would refer to the memory location (or lvalue) that contains the
third element in the array.

To answer your other question &array is just another memory address, see if
this code snippet helps clear up what it points to :)

#include <stdio.h>
#include <stdlib.h>

int array[] = {0, 1, 1, 2, 3, 5, 8, 13, 21};

int main(void) {
printf("&array[2] is: %p\n", &array[2]);
printf("&array[0] is: %p\n", &array[0]);
printf("&array is: %14p\n", &array);
exit(0);
}

% gcc test.c
% ./a.out
&array[2] is: 0x100001088
&array[0] is: 0x100001080
&array is: 0x100001080


Related Topics



Leave a reply



Submit