Because in C, "int *p" has two interpretations.
You can consider P to be a pointer to a single integer, but it can also be considered to be a pointer to an array of integer.
A common example, strings.
char *a = "hello";
"a" is a pointer to a single character, 'h', but more commonly it is treated as a pointer to an array of characters, aka a string.
So, you can do this:
char * a = new char[6];
strcpy(a, "hello");
I believe that your third example is not valid C++.
While this:
p = new int [9][3][4];
Allocates an array of 9 two dimensional arrays of 3x4.
Try this test:
int test()
{
int(* p)[3][4] = new int[1][3][4];
printf("sizeof(p)=%d, sizeof(*p)=%d\n", sizeof(p), sizeof(*p) / sizeof(int));
size_t q1 = (size_t)&p[0][0][0];
size_t q2 = (size_t)&p[1][0][0];
printf("p, diff: %d\n", (q2 - q1) / sizeof(int));
size_t r1 = (size_t)&p[0][0][0];
size_t r2 = (size_t)&p[0][1][0];
printf("p, diff: %d\n", (r2 - r1) / sizeof(int));
size_t s1 = (size_t)&p[0][0][0];
size_t s2 = (size_t)&p[0][0][1];
printf("p, diff: %d\n", (s2 - s1) / sizeof(int));
return 0;
}