Even though they aren't the same thing, there is a close relationship between arrays and pointers in C.  The array subscripting operation a[i] is defined in terms of pointer arithmetic:
a[i] == *(a + i)
That is, take the address a, offset i elements of the array's base type from that address, and dereference the result.  For example, if a is an array of int, the expression a + 1 evaluates to the address of the next integer object following the one stored at a, which may be anywhere from 2, 4, or 8 bytes away.  
Arrays are not pointers; however, except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element in the array.  
So, given the code
int a[10];
a[2] = 2;
In the statement a[2] = 2, the expression a is not the operand of the sizeof or unary & operators, so it is converted ("decays") from type int [10] to int *, and it evaluates to the address of the first element of a.  We then offset 2 elements of type int from that address and assign the value 2 to that location.  Note that this conversion applies to the expression a, not the array object to which it refers.    
So how does all that relate to your code?
In C, a non-bitfield object is composed of a contiguous sequence of one or more bytes.  Objects of char type take up 1 byte by definition, so an object of any multibyte type (int, long, float, double, etc.) can be treated as an array of char (or unsigned char).
A picture may help - I took your code and used a little utility I wrote to dump the contents of each object in memory:
       Item        Address   00   01   02   03
       ----        -------   --   --   --   --
          a 0x7ffffa8a29cc   01   04   00   00    ....
          p 0x7ffffa8a29c0   cc   29   8a   fa    .)..
            0x7ffffa8a29c4   ff   7f   00   00    ....
         p0 0x7ffffa8a29b8   cc   29   8a   fa    .)..
            0x7ffffa8a29bc   ff   7f   00   00    ....
The binary representation of 1025 is 0x0401.  As you can see, the object a is 4 bytes wide and contains the byte sequence {0x01, 0x04, 0x00, 0x00}.  This is on an x86 system, which is little-endian, so the least significant byte comes first in the sequence.  
Both p and p0 store the address of a.  I'm on a 64-bit system, so pointer values are 8 bytes wide.  
*p0 evaluates to the value of first byte in a, which in this case is 0x01.  p0[1] is equivalent to *(p0 + 1), and evaluates to the value of the second character value following p0, which in this case is 0x04.  
     
    
p[x]is just shorthand for*(p + x).00000001 00000100 00000000 00000000. And that's actually what you have, since you got the output1 4. This is because of CPU endianess.