p = array[3]; // int * = int
is an error; the types don't match, and the compiler will yell at you for it.  The type of p is int *, and the type of array[3] is int.  
There are two ways to fix this, depending on what you want to do.  If you want to set p to point to array[3] (which is what you want to do in this case), you would write
p = &array[3]; // int * = int *
If you want to write the value of array[3] to the object that p points to (which is not what you want to do in this case, since p isn't pointing anywhere valid yet), you would write
*p = array[3]; // int = int
In this case, we want to set p to point to array[3], so we use the first statement.  After doing that, the following are true:
 p == &array[3]          // int *
*p ==  array[3] == 2     // int
Now we have the statement
*p = (int) *c;
is saying "take the value of the char object that c points to, convert it to an int value1, and assign the result to the object that p points to."  
Except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize an array of char 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 of the array.
The expression c has type "5-element array of char".  Since it is not the operand of the sizeof or unary & operators, it is converted to an expression of type char *, and the value of the expression is the address of the first element in the array, c[0].  Thus:
 c == &c[0]                         // char *
*c ==  c[0] == 'A' == 65 (ASCII)    // char
Taking all that together, that means
*p = (int) *c;
is another way of writing
*p = (int) c[0];
which is another way of writing
array[3] = (int) c[0];
which is another way of writing
array[3] = (int) 'A';
which is another way of writing
array[3] = 65;
- (int)is a cast expression; it means that the value following it should be treated as type- int.