Basically, variables pointer and pointer2 have different types, however they'll both behave similarly, with the exceptions:
– there will be no array to pointer decay occurring for pointer2 like it does for initial arr, i.e.: e.g.:
#include <stdio.h>
int arr[5] = {1,2,3,4,5};
int *pointer = arr;
int (*pointer2)[5] = &arr;
void func(int *in_arr)
{
   printf("1st: %i, 5th: %i\n", in_arr[0], in_arr[4]);
}
int main(void)
{
    func(pointer);
    func(pointer2); /* generates a warning (in GCC) */
    func(arr);      /* works with no issues*/
}
The warning will be:
ex.c: In function ‘main’:
ex.c:9:14: warning: passing argument 1 of ‘func’ from incompatible pointer type [-Wincompatible-pointer-types]
    9 |         func(pointer2);
      |              ^~~~~~~~
      |              |
      |              int (*)[5]
ex.c:4:24: note: expected ‘int *’ but argument is of type ‘int (*)[5]’
    4 |         void func(int *in_arr)    {
      |                   ~~~~~^~~~~~
– pointer arithmetic will behave differently, pointer+1 will point at second element, while pointer2+1 will point after the array pointer2.
IMO, the (type*)[size] pointers are quite peculiar thing in C. They'll allow, for example, to perform basically a cast to an array type, which is a quite strange thing to do in C – normally, you cannot cast anything to an array type (like.: func( (int [5]) ints_pointer ), however with pointer-type to an array, it's possible to cast to it, so that, e.g.:
// same preamble as in previous example…
void func(int (*in_arr)[5]) {
   printf("1st: %i, 5th: %i\n", ((int *)in_arr)[0], 
        ((int *)in_arr)[4]);
}
int main(void) {
    /* ·•Casts•· to a ·•pointer-to-array•· type to mute warnings */
    func( (int(*)[5]) pointer );
    func( (int(*)[5]) arr );
    /* Generates no warning (in GCC) */
    func(pointer2);
}
works with no issues.