A couple of things...
First, a doesn't store the location of a[0]. There is no object a that is separate from the array element a[0]. Basically what you have in memory is
Address
------- +------+
0x1000 a: | 0x01 | a[0]
+------+
In other words, the address of an array is the same as the address of its first element.
Unless it is the operand of the sizeof or unary & operators, the expression a will be converted ("decay") from type "1-element array of int" (int [1]) to "pointer to int" (int *) and the value of the expression will be the address of the first element in the array.
This means that the expressions &a, a, and &a[0] all yield the same address value; it's just the types of the expressions are different:
Expression Type Decays to
---------- ---- ---------
&a int (*)[1]
a int [1] int *
&a[0] int *
Which brings us to this line:
int* ptr = &arr; // int * = int (*)[1] - assignment of incompatible types
The compiler should have yelled at you about that line. You may want to dial up the warning level.
int* ptr = &arr;- this is incompatible pointer assignment.&arris of type(int*)[1]- that is a pointer to an array. It just happens that the value of it is the same as&arr[0]. So when you dereferenceptrwhich has this value, you are getting the value ofarr[0]int (*)[1]in the warning. Is(int*)[1]the same? I believe not. The parens make the star mean the array not the int, so ratherint (*[])(int*)[1]is a syntax error; the type of&arrisint(*)[1].. The parentheses serve to prevent the token sequenceint *being parsed as type-name