It is a completely superfluous cast that only adds clutter. Or in case of C++ it is needed, but you should never be using malloc in C++...
p is an array pointer to an array of type int [3]. *p is an array and therefore 3*sizeof(*p) gives the size of 3 such arrays, 3*3*sizeof(int). So the code allocates an array of arrays - a 2D array. 
p is set to point at the first element of that array. p[i] is pointer arithmetic on an array pointer and gives you array number i. Therefore a pointer to a 1D array can be used to access a 2D array.
A better way to write the malloc call would be this:
p = malloc( sizeof(int[3][3]) );
You example never initializes the arrays, so printing their contents would give garbage and possibly undefined behavior. Corrected code:
#include <stdio.h>
#include <stdlib.h>
int main (void)
{
     int(*p)[3];
     p = malloc( sizeof(int[3][3]) );
     for(size_t i=0; i<3; i++)
     {
         for(size_t j=0; j<3; j++)
         {
             p[i][j] = (int)j;
             printf("%d ", p[i][j]);
         }
         printf("\n");
     }
     free(p);
     return 0;
}
Related topic: Correctly allocating multi-dimensional arrays.