following on from that,

imagine your program loaded into memory, and running
imagine it as a box

everything inside the box is "your program"
everything outside isn't

Code:
MEMORY

** +---------+
** |******** |
** |** MY****|
** |**PROG** |
** |******** |
** +---------+

** +---------+
** |******** |
** |SOMETHING|
** |** ELSE**|
** |******** |
** +---------+
now, if you have a variable in your program, Eg. Name
and you use normal memory allocation
that variable will be "inside" your program
so it has to have a fixed size

char name[20] ;

Code:
** +--------------------+
** |********************|
** |********************|
** |**[name variable]** |
** |********************|
** +--------------------+
so, if you set the name variable at 20 characters
you will always have 20 characters space for the person's name

If your guy's name is Bob Hope
that's 8 characters
meaning there's 12 characters' worth of wasted space in memory

Code:
****name
****[Bob Hope************]
however, if the guys name is longer than 20
your program won't be able to store it

so, dynamic memory allocation (and pointing to it with pointers)

"inside" your program you have a pointer to a piece of memory "outside" the box

so when the guy inputs his name, a "strip" of memory the exact size to store the inputted name is temporarily "given" to the program

however, it is outside your box, so you have to point to it
using a pointer

Code:
** +--------------------+
** |********************|
** |********************|
** |**[name pointer]****|
** |****** |************|
** +-------|------------+
********** |
**********\|/
** +------------+
** |** Name**** |
** +------------+
So, your program stays as small as possible (no wasted spaces)
and is "given" some memory when you need it
and your program "gives it back" when it's finished with it

All thanks to pointers