Argument of sizeof() - c++

The output of size of for
#include<iostream>
using namespace std;
struct node
{
int k;
struct node *next;
};
int main()
{
int arr[3];
cout<<sizeof(struct node)<<endl;
cout<<sizeof(struct node *)<<endl;
cout<<sizeof(arr)<<endl;
cout<<sizeof(arr[0])<<endl;
cout<sizeof(int *)<<endl;
return 0;
}
is
8
4
12
4
4
I understand that struct node * is a pointer so its output should be 4. So similarly arr is also a pointer, so its output should also be 4 but why is it showing the size of arr array as 12?

So similarly arr is also a pointer
No, arr is not a pointer, it is an array. Although it can be freely converted to a pointer, it is a different kind of object. The results of sizeof indicate the amount of memory necessary to store the array of the specified size, i.e. three times the size of an int.

So similarly arr is also a pointer, so its output should also be 4 but why is it showing the size of arr array as 12?
When array name is an operand of the sizeof or the unary & then it doesn't convert to pointer to its first element. Conversion rule is not applied in these cases.
In case of
cout<<sizeof(arr)<<endl;
The sizeof returns the size of the type of arr, which is of type int[3] (array of 3 ints).
C11: 6.5.3.4:
The sizeof operator yields the size (in bytes) of its operand, which may be an
expression or the parenthesized name of a type. The size is determined from the type of
the operand.

This is because the type of arr is int[3], not int* as you've assumed. So sizeof returns the size of the entire array.

Related

Why sizeof (tab) is 8 bytes when sizeof (int) is 4 bytes? [duplicate]

What is array to pointer decay? Is there any relation to array pointers?
It's said that arrays "decay" into pointers. A C++ array declared as int numbers [5] cannot be re-pointed, i.e. you can't say numbers = 0x5a5aff23. More importantly the term decay signifies loss of type and dimension; numbers decay into int* by losing the dimension information (count 5) and the type is not int [5] any more. Look here for cases where the decay doesn't happen.
If you're passing an array by value, what you're really doing is copying a pointer - a pointer to the array's first element is copied to the parameter (whose type should also be a pointer the array element's type). This works due to array's decaying nature; once decayed, sizeof no longer gives the complete array's size, because it essentially becomes a pointer. This is why it's preferred (among other reasons) to pass by reference or pointer.
Three ways to pass in an array1:
void by_value(const T* array) // const T array[] means the same
void by_pointer(const T (*array)[U])
void by_reference(const T (&array)[U])
The last two will give proper sizeof info, while the first one won't since the array argument has decayed to be assigned to the parameter.
1 The constant U should be known at compile-time.
Arrays are basically the same as pointers in C/C++, but not quite. Once you convert an array:
const int a[] = { 2, 3, 5, 7, 11 };
into a pointer (which works without casting, and therefore can happen unexpectedly in some cases):
const int* p = a;
you lose the ability of the sizeof operator to count elements in the array:
assert( sizeof(p) != sizeof(a) ); // sizes are not equal
This lost ability is referred to as "decay".
For more details, check out this article about array decay.
Here's what the standard says (C99 6.3.2.1/3 - Other operands - Lvalues, arrays, and function designators):
Except when it is the operand of the sizeof 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.
This means that pretty much anytime the array name is used in an expression, it is automatically converted to a pointer to the 1st item in the array.
Note that function names act in a similar way, but function pointers are used far less and in a much more specialized way that it doesn't cause nearly as much confusion as the automatic conversion of array names to pointers.
The C++ standard (4.2 Array-to-pointer conversion) loosens the conversion requirement to (emphasis mine):
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.”
So the conversion doesn't have to happen like it pretty much always does in C (this lets functions overload or templates match on the array type).
This is also why in C you should avoid using array parameters in function prototypes/definitions (in my opinion - I'm not sure if there's any general agreement). They cause confusion and are a fiction anyway - use pointer parameters and the confusion might not go away entirely, but at least the parameter declaration isn't lying.
"Decay" refers to the implicit conversion of an expression from an array type to a pointer type. In most contexts, when the compiler sees an array expression it converts the type of the expression from "N-element array of T" to "pointer to T" and sets the value of the expression to the address of the first element of the array. The exceptions to this rule are when an array is an operand of either the sizeof or & operators, or the array is a string literal being used as an initializer in a declaration.
Assume the following code:
char a[80];
strcpy(a, "This is a test");
The expression a is of type "80-element array of char" and the expression "This is a test" is of type "15-element array of char" (in C; in C++ string literals are arrays of const char). However, in the call to strcpy(), neither expression is an operand of sizeof or &, so their types are implicitly converted to "pointer to char", and their values are set to the address of the first element in each. What strcpy() receives are not arrays, but pointers, as seen in its prototype:
char *strcpy(char *dest, const char *src);
This is not the same thing as an array pointer. For example:
char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;
Both ptr_to_first_element and ptr_to_array have the same value; the base address of a. However, they are different types and are treated differently, as shown below:
a[i] == ptr_to_first_element[i] == (*ptr_to_array)[i] != *ptr_to_array[i] != ptr_to_array[i]
Remember that the expression a[i] is interpreted as *(a+i) (which only works if the array type is converted to a pointer type), so both a[i] and ptr_to_first_element[i] work the same. The expression (*ptr_to_array)[i] is interpreted as *(*a+i). The expressions *ptr_to_array[i] and ptr_to_array[i] may lead to compiler warnings or errors depending on the context; they'll definitely do the wrong thing if you're expecting them to evaluate to a[i].
sizeof a == sizeof *ptr_to_array == 80
Again, when an array is an operand of sizeof, it's not converted to a pointer type.
sizeof *ptr_to_first_element == sizeof (char) == 1
sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size
is on your platform
ptr_to_first_element is a simple pointer to char.
Arrays, in C, have no value.
Wherever the value of an object is expected but the object is an array, the address of its first element is used instead, with type pointer to (type of array elements).
In a function, all parameters are passed by value (arrays are no exception). When you pass an array in a function it "decays into a pointer" (sic); when you compare an array to something else, again it "decays into a pointer" (sic); ...
void foo(int arr[]);
Function foo expects the value of an array. But, in C, arrays have no value! So foo gets instead the address of the first element of the array.
int arr[5];
int *ip = &(arr[1]);
if (arr == ip) { /* something; */ }
In the comparison above, arr has no value, so it becomes a pointer. It becomes a pointer to int. That pointer can be compared with the variable ip.
In the array indexing syntax you are used to seeing, again, the arr is 'decayed to a pointer'
arr[42];
/* same as *(arr + 42); */
/* same as *(&(arr[0]) + 42); */
The only times an array doesn't decay into a pointer are when it is the operand of the sizeof operator, or the & operator (the 'address of' operator), or as a string literal used to initialize a character array.
It's when array rots and is being pointed at ;-)
Actually, it's just that if you want to pass an array somewhere, but the pointer is passed instead (because who the hell would pass the whole array for you), people say that poor array decayed to pointer.
Array decaying means that, when an array is passed as a parameter to a function, it's treated identically to ("decays to") a pointer.
void do_something(int *array) {
// We don't know how big array is here, because it's decayed to a pointer.
printf("%i\n", sizeof(array)); // always prints 4 on a 32-bit machine
}
int main (int argc, char **argv) {
int a[10];
int b[20];
int *c;
printf("%zu\n", sizeof(a)); //prints 40 on a 32-bit machine
printf("%zu\n", sizeof(b)); //prints 80 on a 32-bit machine
printf("%zu\n", sizeof(c)); //prints 4 on a 32-bit machine
do_something(a);
do_something(b);
do_something(c);
}
There are two complications or exceptions to the above.
First, when dealing with multidimensional arrays in C and C++, only the first dimension is lost. This is because arrays are layed out contiguously in memory, so the compiler must know all but the first dimension to be able to calculate offsets into that block of memory.
void do_something(int array[][10])
{
// We don't know how big the first dimension is.
}
int main(int argc, char *argv[]) {
int a[5][10];
int b[20][10];
do_something(a);
do_something(b);
return 0;
}
Second, in C++, you can use templates to deduce the size of arrays. Microsoft uses this for the C++ versions of Secure CRT functions like strcpy_s, and you can use a similar trick to reliably get the number of elements in an array.
tl;dr: When you use an array you've defined, you'll actually be using a pointer to its first element.
Thus:
When you write arr[idx] you're really just saying *(arr + idx).
functions never really take arrays as parameters, only pointers - either directly, when you specify an array parameter, or indirectly, if you pass a reference to an array.
Sort-of exceptions to this rule:
You can pass fixed-length arrays to functions within a struct.
sizeof() gives the size taken up by the array, not the size of a pointer.
Try this code
void f(double a[10]) {
printf("in function: %d", sizeof(a));
printf("pointer size: %d\n", sizeof(double *));
}
int main() {
double a[10];
printf("in main: %d", sizeof(a));
f(a);
}
and you will see that the size of the array inside the function is not equal to the size of the array in main, but it is equal to the size of a pointer.
You probably heard that "arrays are pointers", but, this is not exactly true (the sizeof inside main prints the correct size). However, when passed, the array decays to pointer. That is, regardless of what the syntax shows, you actually pass a pointer, and the function actually receives a pointer.
In this case, the definition void f(double a[10] is implicitly transformed by the compiler to void f(double *a). You could have equivalently declared the function argument directly as *a. You could have even written a[100] or a[1], instead of a[10], since it is never actually compiled that way (however, you shouldn't do it obviously, it would confuse the reader).
Arrays are automatically passed by pointer in C. The rationale behind it can only be speculated.
int a[5], int *a and int (*a)[5] are all glorified addresses meaning that the compiler treats arithmetic and deference operators on them differently depending on the type, so when they refer to the same address they are not treated the same by the compiler. int a[5] is different to the other 2 in that the address is implicit and does not manifest on the stack or the executable as part of the array itself, it is only used by the compiler to resolve certain arithmetic operations, like taking its address or pointer arithmetic. int a[5] is therefore an array as well as an implicit address, but as soon as you talk about the address itself and place it on the stack, the address itself is no longer an array, and can only be a pointer to an array or a decayed array i.e. a pointer to the first member of the array.
For instance, on int (*a)[5], the first dereference on a will produce an int * (so the same address, just a different type, and note not int a[5]), and pointer arithmetic on a i.e. a+1 or *(a+1) will be in terms of the size of an array of 5 ints (which is the data type it points to), and the second dereference will produce the int. On int a[5] however, the first dereference will produce the int and the pointer arithmetic will be in terms of the size of an int.
To a function, you can only pass int * and int (*)[5], and the function casts it to whatever the parameter type is, so within the function you have a choice whether to treat an address that is being passed as a decayed array or a pointer to an array (where the function has to specify the size of the array being passed). If you pass a to a function and a is defined int a[5], then as a resolves to an address, you are passing an address, and an address can only be a pointer type. In the function, the parameter it accesses is then an address on the stack or in a register, which can only be a pointer type and not an array type -- this is because it's an actual address on the stack and is therefore clearly not the array itself.
You lose the size of the array because the type of the parameter, being an address, is a pointer and not an array, which does not have an array size, as can be seen when using sizeof, which works on the type of the value being passed to it. The parameter type int a[5] instead of int *a is allowed but is treated as int * instead of disallowing it outright, though it should be disallowed, because it is misleading, because it makes you think that the size information can be used, but you can only do this by casting it to int (*a)[5], and of course, the function has to specify the size of the array because there is no way to pass the size of the array because the size of the array needs to be a compile-time constant.
I might be so bold to think there are four (4) ways to pass an array as the function argument. Also here is the short but working code for your perusal.
#include <iostream>
#include <string>
#include <vector>
#include <cassert>
using namespace std;
// test data
// notice native array init with no copy aka "="
// not possible in C
const char* specimen[]{ __TIME__, __DATE__, __TIMESTAMP__ };
// ONE
// simple, dangerous and useless
template<typename T>
void as_pointer(const T* array) {
// a pointer
assert(array != nullptr);
} ;
// TWO
// for above const T array[] means the same
// but and also , minimum array size indication might be given too
// this also does not stop the array decay into T *
// thus size information is lost
template<typename T>
void by_value_no_size(const T array[0xFF]) {
// decayed to a pointer
assert( array != nullptr );
}
// THREE
// size information is preserved
// but pointer is asked for
template<typename T, size_t N>
void pointer_to_array(const T (*array)[N])
{
// dealing with native pointer
assert( array != nullptr );
}
// FOUR
// no C equivalent
// array by reference
// size is preserved
template<typename T, size_t N>
void reference_to_array(const T (&array)[N])
{
// array is not a pointer here
// it is (almost) a container
// most of the std:: lib algorithms
// do work on array reference, for example
// range for requires std::begin() and std::end()
// on the type passed as range to iterate over
for (auto && elem : array )
{
cout << endl << elem ;
}
}
int main()
{
// ONE
as_pointer(specimen);
// TWO
by_value_no_size(specimen);
// THREE
pointer_to_array(&specimen);
// FOUR
reference_to_array( specimen ) ;
}
I might also think this shows the superiority of C++ vs C. At least in reference (pun intended) of passing an array by reference.
Of course there are extremely strict projects with no heap allocation, no exceptions and no std:: lib. C++ native array handling is mission critical language feature, one might say.

Why does the sizeof operator produce different results for an array

Why does the sizeof operator produces 12 bytes when it should only be 4? When I reference the variable array, that is only referring to the memory address of the first index of the array. In fact, I printed the memory address of the first index &array[0] and compared it to array, they produced the same memory address result which confirms that they are both referring to the first index of the array, but 'array' produces 12 byte while array[0] produces 4 byte.
int main() {
int array[] = {1,2,3};
int a = 1;
int b = sizeof(array); //this is referring to the first index of the array
int c = sizeof(array[0]); //this is also referring to the first index of the array
std::cout << b << std::endl;
std::cout << array << std::endl; //they have the same memory address
std::cout << &array[0] << std::endl; /* they have the same memory address, which confirms that array
and &array[0] is the same */
return 0;
}
Arrays and pointers are not the same, and this is a prime example of this.
In most contexts, an array decays to a pointer to its first member. One of the few times this decay does not happen is when the array is the subject of the sizeof operator. In that case it refers to the entire array and the expression evaluates to the size of the entire array in bytes.
This is described in section 6.3.2.1p3 of the C standard:
Except when it is the operand of the sizeof operator, the _Alignof operator, or theunary & 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. If the array object has register storage class, the behavior is undefined.
As well as the C++11 standard in sections 7.2:
An lvalue or rvalue of type “array of N T” or “array of unknown bound of T” can be converted to a prvalue of type “pointer to T”. The temporary materialization conversion (7.4) is applied. The result is a pointer to the first element of the array.
And 8.3.3p4:
The lvalue-to-rvalue (7.1), array-to-pointer (7.2), and function-to-pointer (7.3) standard conversions are not applied to the operand of sizeof. If the operand is a prvalue, the temporary materialization conversion (7.4)is applied.
So what we actually have is:
int b = sizeof(array); // size of the entire array
int c = sizeof(array[0]); // size of the first element of the array
int d = sizeof(&array[0]); // size of a pointer to an array element
The size of the array is 12 bytes. The output is correct. The size of the element is 4 bytes. There are 3 elements. 4 * 3 = 12.
In fact, i printed the memory address of the first index (&array[0]) and compared it to (array), they produced the same memory address result which confirms that they are both referring to the first index of the array
Just because the array has the same memory address as the first element of the array, doesn't mean that the entire array is contained within the first element. It isn't.
then why does array+1 refers to the second index of the array?
Because in such a sub expression, the array is implicitly converted to a pointer to first element, and adding 1 to a pointer to first element results in a pointer to second element. Such implicit conversion is called decaying.
If I understand your question well, on 64 bit machine int will be 4 bytes so the sizeof operator will return 3 x 4 = 12 bytes. Did not understand your assumption why it should return 4 in first place when you allocated only three items in that array.
If you want to know the number of items in the array you may do something as follows:
// Finds size of array[] and stores in 'size'
int size = sizeof(array)/sizeof(array[0]);

Pointer to arrays syntax

I have a question about syntax of pointer to arrays.
Well we know arrays are pointers themselves(what our uni professor said) so why when we point to them with another pointer (which would be a pointer to pointer) we use this syntax:
int array[10];
int *pointer = array;
Instead of this syntax:
int array[10];
int **pointer = &array;
Although i know this would be correct using malloc but why not in the normal way, is it a compiler or syntax thing or i am wrong somewhere else??
Tell your professor they are wrong. Arrays are not pointers. Arrays can decay to pointers, but they are not pointers.
int* pointer = array; declares a pointer that points to the first element in array.
int** pointer = &array; is not correct. As mentioned by jschultz410 in the comments, the type of &array is not int**, it is int (*)[10] aka a pointer to an array of 10 ints, which cannot decay to int**.
Well we know arrays are pointers themselves
No. Arrays are not pointers. Arrays are arrays. Except when it is the operand of the sizeof or the unary & operator, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer toT" and the value of the expression will be the address of the first element of the array. However, no storage is set aside for a pointer in addition to the array elements themselves.
So, given the declaration
int array[10];
the type of the expression array is "10-element array of int"; unless array is the operand of sizeof or unary &, it will decay to type int *. So
int *ptr = array;
works.
The type of &array is not int **; the type is int (*)[10], or "pointer to 10-element array of int". You'd declare and initialize such a pointer as
int (*ptr)[10] = &array;
First of all this definition of a pointer
int array[10];
int **pointer = &array;
is invalid. In the right side of the declaration there is an expression having type int ( * )[10] while in the left side there is identifier of type int **. There is no implicit conversion between pointers int ( * )[10] and int ** . So the compiler shall issue a diagnostic message.
The correct definition will look
int array[10];
int ( *pointer )[10] = &array;
Now we can consider what is the difference between these two definitions
int array[10];
int *pointer = array;
and
int array[10];
int ( *pointer )[10] = &array;
In the first case the size of the object pointed to by pointer pointer is equal to sizeof( int ). So if to use the pointer arithmetic then after evaluation of expression ++pointer the value in the pointer will be increased by sizeof( int ) bytes. If for example sizeof( int ) is equal to 4 then the value in the pointer will be increased by 4.
In the second case the size of the object pointed to by pointer pointer is equal to 10 * sizeof( int ) that is if sizeof( int ) is equal to 4 then the size of the object is equal to 40. So if the pointer will be increased ++pointer its value will be increased by 40.
Also dereferencing the pointer in the first case will give you an object of type int while dereferencing the pointer in the second case will give you an object of type int[10] that is an array.
And arrays are not pointers. Simply they are usually converted to pointers to their first elements in expressions. From the C Standard
3 Except when it is the operand of the sizeof 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. If the array object
has register storage class, the behavior is undefined.
If you write for example
int array[10];
int *pointer = array;
then sizeof( array ) is not equal to sizeof( pointer ) though you can use the common syntax to access elements of the array:
array[ i ] and pointer[ i ] and will get the same result..

How does sizeof know the size of array? [duplicate]

This question already has answers here:
How does sizeof know the size of the operand array?
(12 answers)
Closed 8 years ago.
I have codes as following:
main() {
int array[5] = {3,6,9,-8,1};
printf("the size of the array is %d\n", sizeof(array));
printf("the address of array is %p\n", array);
printf("the address of array is %p\n", &array);
int * x = array;
printf("the address of x is %p\n", x);
printf("the size of x is %d\n", sizeof(x));
}
The output is
the size of the array is 20
the address of array is 0x7fff02309560
the address of array is 0x7fff02309560
the address of x is 0x7fff02309560
the size of x is 8
I know the variable array will be seen as a pointer to the first element of the array, so I understand the the size of x is 8. But I don't know why the size of the array is 20. Isn't it should be 8 (in a 64-bits machine)?
Besides how does the program know that it is 20? As far as I know in C it doesn't store the number of elements. How come the sizeof(array) and sizeof(x) is different? I tracked several posts pertaining to array decaying but no idea on this problem.
The name of an array decays to a pointer to the first element of the array in most situations. There are a couple of exceptions to that rule though. The two most important are when the array name is used as the operand of either the sizeof operator or the address-of operator (&). In these cases, the name of the array remains an identifier for the array as a whole.
For a non-VLA array, this means that the size of the array can be determined statically (at compile time) and the result of the expression will be the size of the array (in bytes), not the size of a pointer.
When you take the address of the array, you get the same value (i.e., the same address) as if you'd just used the name of the array without taking the address. The type is different though--when you explicitly take the address, what you get is a pointer of type "pointer to array of N items of type T". That means (for one example) that while array+1 points to the second element of the array, &array+1 points to another array just past the end of the entire array.
Assuming an array of at least two items, *(array+1) will refer to the second element of the array. Regardless of the array size, &array+1 will yield an address past the end of the array, so attempting to dereference that address gives undefined behavior.
In your case, given that the size of the array is 20, and the size of one element of the array is 4, if array was, say, 0x1000, then array+1 would be 0x1004 and &array+1 would be 0x1014 (0x14 = 20).
Your array has a static length so it can be determined at compile time. Your compiler knows the sizeof(int) = 4 and your static array length [5]. 4 * 5 = 20
Edit: Your compilers int is probably 32-bit, but addressing 64-bit. That is why sizeof(pointer) returns 8.
Note that sizeof is not a library function. sizeof is
a compile-time unary operator [...] that can be used to compute the
size of any object K&R
So sizeof doesn't know how big the array is, the compiler knows how big the array is, and by definition
when applied to an array, the result is the total number of bytes
in the array.K&R
A pointer and an array are 2 different data types.
Array can hold elements of similar data type. The memory for array is contiguous.
Pointer is used to point to some valid memory location.
sizeof(type) gives you the number of bytes of the type you pass.
Now if you pass array then the compiler knows that this is an array and number of elements in it and it just multiplies that many elements with the respective data-type size value.
In this case:
5*4 = 20
Again the sizeof(int) or sizeof(pointer) is platform dependent. In this case you are seeing sizeof(pointer) as 8.
No, arrays do not decay as operands of the sizeof operator. This is one of the few places where arrays don't decay. If an int is 4 bytes on your machine, then the total number of bytes of the array should be 20 (4 * 5). We don't even need an object to test this.
sizeof(int[5]) // 20
sizeof(int*) // 8 on a 64-bit machine
C11: 6.5.3.4 (p2)
The sizeof operator yields the size (in bytes) of its operand, which may be an
expression or the parenthesized name of a type. The size is determined from the type of
the operand. [...]
In the declaration
int array[5]
the type of array is an array of 5 ints. The compiler will determine the size of array from this type.
Try this
int x = sizeof(array)/sizeof(int);
printf("the size of the array is %d\n", x);

Why is arr and &arr the same?

I have been programming c/c++ for many years, but todays accidental discovery made me somewhat curious... Why does both outputs produce the same result in the code below? (arr is of course the address of arr[0], i.e. a pointer to arr[0]. I would have expected &arr to be the adress of that pointer, but it has the same value as arr)
int arr[3];
cout << arr << endl;
cout << &arr << endl;
Remark: This question was closed, but now it is opened again. (Thanks ?)
I know that &arr[0] and arr evaluates to the same number, but that is not my question! The question is why &arr and arr evaluates to the same number. If arr is a literal (not stored anyware), then the compiler should complain and say that arr is not an lvalue. If the address of the arr is stored somewhere then &arr should give me the address of that location. (but this is not the case)
if I write
const int* arr2 = arr;
then arr2[i]==arr[i] for any integer i, but &arr2 != arr.
#include <cassert>
struct foo {
int x;
int y;
};
int main() {
foo f;
void* a = &f.x;
void* b = &f;
assert(a == b);
}
For the same reason the two addresses a and b above are the same. The address of an object is the same as the address of its first member (Their types however, are different).
arr
_______^_______
/ \
| [0] [1] [2] |
--------------------+-----+-----+-----+--------------------------
some memory | | | | more memory
--------------------+-----+-----+-----+--------------------------
^
|
the pointers point here
As you can see in this diagram, the first element of the array is at the same address as the array itself.
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.
The two have the same value but different types.
When it's used by itself (not the operand of & or sizeof), arr evaluates to a pointer to int holding the address of the first int in the array.
&arr evaluates to a pointer to array of three ints, holding the address of the array. Since the first int in the array has to be at the very beginning of the array, those addresses must be equal.
The difference between the two becomes apparent if you do some math on the results:
arr+1 will be equal to arr + sizeof(int).
((&arr) + 1) will be equal to arr + sizeof(arr) == arr + sizeof(int) * 3
Edit: As to how/why this happens, the answer is fairly simple: because the standard says so. In particular, it says (§6.3.2.1/3):
Except when it is the operand of the sizeof 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.
[note: this particular quote is from the C99 standard, but I believe there's equivalent language in all versions of both the C and C++ standards].
In the first case (arr by itself), arr is not being used as the operand of sizeof, unary &, etc., so it is converted (not promoted) to the type "pointer to type" (in this case, "pointer to int").
In the second case (&arr), the name obviously is being used as the operand of the unary & operator -- so that conversion does not take place.
The address is the same but both expressions are different. They just start at the same memory location. The types of both expressions are different.
The value of arr is of type int * and the value of &arr is of type int (*)[3].
& is the address operator and the address of an object is a pointer to that object. The pointer to an object of type int [3] is of type int (*)[3]
They are not the same.
A bit more strict explanation:
arr is an lvalue of type int [3]. An attempt to use
arr in some expressions like cout << arr will result in lvalue-to-rvalue conversion which, as there are no rvalues of array type, will convert it to an rvalue of type int * and with the value equal to &arr[0]. This is what you can display.
&arr is an rvalue of type int (*)[3], pointing to the array object itself. No magic here :-) This pointer points to the same address as &arr[0] because the array object and its first member start in the exact same place in the memory. That's why you have the same result when printing them.
An easy way to confirm that they are different is comparing *(arr) and *(&arr): the first is an lvalue of type int and the second is an lvalue of type int[3].
Pointers and arrays can often be treated identically, but there are differences. A pointer does have a memory location, so you can take the address of a pointer. But an array has nothing pointing to it, at runtime. So taking the address of an array is, to the compiler, syntactically defined to be the same as the address of the first element. Which makes sense, reading that sentence aloud.
I found Graham Perks' answer to be very insightful, I even went ahead and tested this in an online compiler:
int main()
{
int arr[3] = {1,2,3};
int *arrPointer = arr; // this is equivalent to: int *arrPointer = &arr;
printf("address of arr: %p\n", &arr);
printf("address of arrPointer: %p\n", &arrPointer);
printf("arr: %p\n", arr);
printf("arrPointer: %p\n", arrPointer);
printf("*arr: %d\n", *arr);
printf("*arrPointer: %d\n", *arrPointer);
return 0;
}
Outputs:
address of arr: 0x7ffed83efbac
address of arrPointer: 0x7ffed83efba0
arr: 0x7ffed83efbac
arrPointer: 0x7ffed83efbac
*arr: 1
*arrPointer: 1
It seems the confusion was that arr and arrPointer are equivalent. However, as Graham Parks detailed in his answer, they are not.
Visually, the memory looks something like this:
[Memory View]
[memory address: value stored]
arrPointer:
0x7ffed83efba0: 0x7ffed83efbac
arr:
0x7ffed83efbac: 1
0x7ffed83efbb0: 2
0x7ffed83efbb4: 3
As you can see, arrPointer is a label for memory address 0x7ffed83efba0 which has 4 bytes of allocated memory which hold the memory address of arr[0].
On the other hand, arr is a label for memory address 0x7ffed83efbac, and as per Jerry Coffin's answer, since the type of variable arr is "array of type", it gets converted to a "pointer of type" (which points to the array's starting address), and thus printing arr yields 0x7ffed83efbac.
The key difference is arrPointer is an actual pointer and has its own memory slot allocated to hold the value of the memory it's pointing to, so &arrPointer != arrPointer. Since arr is not technically a pointer but an array, the memory address we see when printing arr is not stored elsewhere, but rather determined by the conversion mentioned above. So, the values (not types) of &arrPointer and arrPointer are equal.