if p is defined as int *p  then  
[p] is wrong! cause an error: expected expression before ‘[’ token.    
Where as 0[p] is correct!   
And 0[p] is same as  p[0] , similarly p[4]  is 4[p].       
compiler convert p[4] into *(p + 4)  that as   *(4 + p)  => 4[p] 
Additionally, suppose if you have an array say int a[10], you can access elements of array either as a[i] or i[a] 
following example will be useful, I think:      
int main(){
    int a[5] = {1,2,3,4,5};
    int* p;  // similar declaration of p (you asked)
    p = a;
    int i= 0; 
    for(i=0; i < 5; i++){
        printf("a[i] =  %d  and i[a] = %d \n",a[i],i[a]);
    }
    printf(" using p \n"); // access using pointer.  
    for(i=0; i < 5; i++){
        printf("p[i] =  %d  and i[p] = %d \n",p[i],i[p]);
    }
}    
compile and execution:     
:~$ ./a.out 
a[i] =  1  and i[a] = 1 
a[i] =  2  and i[a] = 2 
a[i] =  3  and i[a] = 3 
a[i] =  4  and i[a] = 4 
a[i] =  5  and i[a] = 5 
 using p 
p[i] =  1  and i[p] = 1 
p[i] =  2  and i[p] = 2 
p[i] =  3  and i[p] = 3 
p[i] =  4  and i[p] = 4 
p[i] =  5  and i[p] = 5 
[ANSWER-2 ] 
A declaration void (*xyz)(void);  creates xyz a pointer to function that returns void and arguments are void. (xyz is not a data-type but a pointer variable) e.g.      
void function(void){
 // definition 
}
void (*xyz)(void);   
then xyz can be assigned address of function:      
xyz = function;   
And using xyz() you can call function(),  A example for void (*xyz)(void):        
#include<stdio.h>
void function(void){
    printf("\n An Example\n");
} 
int main(){
    void (*xyz)(void);   
    xyz = function;    
    xyz();
}
Now compile and execute it:     
:~$ gcc  x.c
:~$ ./a.out 
 An Example
:~$