Difference in types
The type of p in
int (*p)[3] = &arr;
is int (*)[3], i.e. a pointer to an array of 3 ints.
The type of p in:
int *p = arr;
is simply int*, i.e. a pointer to an int.
As a consequence,
In the first case,
*p evaluates to an array of 3 ints, i.e. int [3].
In the second case,
*p evaluates to just an int.
To get the first element of arr, you'll have to use (*p)[0] or p[0][0] in the first case.
To get the first element of arr, you'll have to use *p or p[0] in the second case.
To get the last element of arr, you'll have to use (*p)[2] or p[0][2] in the first case.
To get the last element of arr, you'll have to use *(p+2) or p[2] in the second case.