Here is a simplistic explanation...
Try and imagine a computer with only five blocks of memory.
If, in a C program, you make the declaration..
int MyInt;
...the first block is made available to hold the integer value you want to assign to MyInt. Anytime you adjust the value of MyInt, the value in block one will be changed. MyInt always exists in block one.
The same applies if you were to declare...
int ManyInts[4];
The array ManyInts can hold four individual int values, and it will hold those values in blocks one to four. ManyInts will always relate to these four blocks.
If you were to declare...
int *pInt;
...block one would be assigned to the pointer-to-int, but the actual value it held (after assignment) would be another block's address (i.e. either block2, block3, block4 or block5]. You could then point the [pointer to any of the values in the other blocks of memory simply by changing the value in block1. The value of the integer in whichever block pInt points to can be found by de-referencing the pointer after it has been pointed.
I think the key thing to remember is that a true variable holds an actual value, while a pointer holds the memory address of another variable.
Code:
#include <stdio.h>
int
main( int argc, char *argv[] )
{
int int1=1;
int *pInt;
pInt = &int1; /* The '&' provides the memory address */
/* of the int1 variable */
/* NOTE: pInt = int1 would be wrong */
printf( "\n%d\n", *pInt ); /* The '*' de-references the pointer */
printf( "\n%d\n", pInt ); /* This will print the memory address */
/* of int1 (you would not normally do */
/* this, and %d might not be the right */
/* thing to use if you did - only do it */
/* when you are sure you know what */
/* you're doing) */
}
Obviously memory management is not as simple as block1 block2 etc, but I hope I made the point.