In your particular example
int arr[10]
int (*ptr)[10];
ptr = & arr; //points to the base address of an array of 10 integers
for (i = 0; i < 10; i++) //prints the values
    printf("%d\n", *(*ptr + i));  //NOTE: same as `ptr[0][i]`
there's no advantage or at least no advantage being utilized.
*(*ptr+i) with int (*ptr)[10]; will/should generate the same assembly as
*(ptr+i) would with int ptr[10]; (Note: some/many might find ptr[0][i] and ptr[i] respectively as more readable renderings of these expressions)
Example:
int get_nth(int (*X)[10], int N) { return *(*X+N); }
int get_nth_(int *X, int N) { return *(X+N); }
x86_64  output (gcc -O3 or clang -O3):
get_nth:                                # @get_nth
        movsxd  rax, esi
        lea     rax, [rdi + 4*rax]
        ret
get_nth_:                               # @get_nth_
        movsxd  rax, esi
        lea     rax, [rdi + 4*rax]
        ret
https://gcc.godbolt.org/z/up7HXc
If int (*ptr)[10] were derived from a multidimensional array
as in
int multidim[5][10]; //array 10 or array 5 of int
int (*ptr)[10]=&multidim[1];
you could use the first index to jump the pointer in increments of 10*sizeof(int) in addition to using the second one to jump in increments of sizeof(int) (as with a plain int*).
In a standalone example (i.e., where the 10-int block isn't part of a multidimensional array), probably the only "advantage" of int(*)[10] is that it retains the sizeof information (even across a function call boundary), and you could conceivably use this for explicit bounds-checking.
Bounds-checking example:
#include <stdio.h>
#define CHECKED_SUBSCRIPT(PtrArray, Index) (*(PtrArray))[BOUNDCHECK(Index,ARRCNT(*(PtrArray)))] /*{{{*/
    #include <assert.h>
    static inline size_t BOUNDCHECK(size_t Index, size_t Bound)
    {
        assert(Index <  Bound);
        return Index;
    }
        //sizeof(A) or void (=>error) if A isn't an array
        #define ARRSZ(A) (+ sizeof(A) + 0* /*{{{*/\
                     _Generic((__typeof(&(A)[0])*)0, __typeof(__typeof(A)*):(void)0,default:0) ) /*}}}*/
    #define ARRCNT(A) (ARRSZ(A)/sizeof((A)[0])) /*}}}*/
int main()
{
    int arr[10]={0,2,4,6,8,10,12,14,16,18};
    int (*ptr)[10] = &arr;
    for (int i = 0; i < 20; i++){
        printf("%d\n", CHECKED_SUBSCRIPT(ptr,i));
    }
}
Output:
0
2
4
6
8
10
12
14
16
18
a.out: boundschecking.c:7: BOUNDCHECK: Assertion `Index < Bound' failed.