The difference between these two declarations
int *p = arr;
int (*r)[3] = arr;
is that in the second declaration there is used a wrong initializer. The array arr used as an initializer is implicitly converted to pointer to its first element of the type int *. So in the second declaration the initialized object and the initializer have different pointer types and there is no implicit conversion between the types.
To make the second declaration correct you need to write
int (*r)[3] = &arr;
Now the both pointers p and r stores the same value: the address of the extent of memory occupied by the array but have different types.
For example if you will write
printf( "sizeof( *p ) = %zu\n", sizeof( *p ) );
printf( "sizeof( *r ) = %zu\n", sizeof( *r ) );
then the first call will output the size of an object of the type int that is equal to 4 while the second call will output the size of the whole array of the type int[3] that is equal to 12.
In this your call of printf
printf("%u %u", p, r);
there are used incorrect conversion specifiers with pointers. Instead you have to write
printf("%p %p", ( void * )p, ( void * )r);
The expressions r[0], r[1], r[2] have the type int[3]. Used in this call
printf("\n%d %d %d", r[0], r[1], r[2]);
they as it was mentioned are implicitly converted to pointers to their first elements. But these arrays except the array r[0] that denotes the array arr do not exist. So there takes place an access to memory beyond the array arr,
You could write
printf( "\n%p\n", ( void * )r[0] );
and this call will be equivalent to
printf("\n%p\n", ( void * )arr );
This call of printf
printf("\n%d %d %d", *r[0], *r[1], *r[2]);
is also incorrect because arrays r[1] and r[2] of the type int[3] do not exist.
This call of printf
printf("\n%d %d %d", (*r)[0], (*r)[1], (*r)[2]);
outputs elements of the array arr due to using the expression *r.