This
int (*p)[R][C];
declares a pointer to an array having the type int[R][C]. The result of the expression sizeof( int[R][C] ) is equal to R * C * sizeof( int ).
The expression *p has the type int[R][C]. So sizeof( *p ) is equivalent to sizeof( int[R][C] ).
As for this declaration
int p[R][C];
then the array designator p in the expression *p is converted to pointer to its first element and dereferencing the pointer you will get the first element of the array that has the type int[C].
So for the first declaration of the variable p as a pointer sizeof( *o ) is equal to sizeof( int[R][C].
For the second declaration of the variable p as a two-dimensional array sizeof( *p ) is equal to sizeof( int[C] ).
Using the first declaration of p as a pointer you could get the same result as for p declared as an array if you wrote
sizeof( **p )
That is dereferencing the pointer the first time you get an object of the type that is used in the second declaration to declare the variable p as an array.
Pay attention to that this call
printf("%d", sizeof(*p));
has undefined behavior because there is used a wrong conversion format. You have to write
printf("%zu", sizeof(*p));
printf("%d", sizeof(*p));is wrong.sizeof()returnssize_t, and the correct format specifier forsize_tis%zu.