All of these answers do a good job of explaining what pointers are, but perhaps don't really explain WHY you need to use them.
Here's why: C is only a single step up from assembly language. Granted, it's a big step, but you wouldn't be too far off if you thought of C as sort of a meta-assembler, as compared to a higher level language like Python, Ruby, or Java. In those languages, you generally don't need pointers, because the language takes care of managing memory and handling more complex data types such as strings for you. By comparison, C pretty much hands you a chunk of memory and says Good luck!
There are some macros and functions built into the language to simplify this task, but by and large you're responsible for handling memory yourself. The first simplification, and the one you're already familiar with, are variables. When you declare a variable, you're essentially delegating management of that tiny chunk of memory to the C compiler. You're saying, Whenever I talk about the variable foo, figure out what part of memory you assigned to that variable and grab whatever is in itI don't care how you do it. The C compiler responds by allocating this memory on what is called the stack, based on the type of variable and how long you need it for (that is what is called the scope of the variable). The stack is nice because it is handled automatically, but there are two problems with it: You need to know in advance how much memory you plan to use, and there isn't that much stack memory available.
And then there's the heap. You know all of that RAM you have installed in your computer? That's the heap. In the old days, before protected memory, this was literally true, but modern operating systems actually abstract it away from the C compiler and give each process its own virtual heap, so to speak, in order to prevent different programs from trampling all over each other (this was a very common problem under MacOS 9 and below, which is why a single program crashing would often bring down the system).
The heap is good for storing large chunks of data, but you can't declare how much heap you need in advance of running the program. Instead, you request heap memory at runtime by using the malloc() statement, which is so primitive that it doesn't even know or care what type of data you're requesting. Hence, you will see statements like ptr = malloc(500 * sizeof(int) ), which requests enough memory to store 500 integers, however large an integer happens to be. How do you reference this memory? The malloc statement returns a pointer to it. Where is this pointer stored? Why, in a stack variable! So you can think about it as using a stack variable, which is your most basic, primitive sort of variable, to store an address to your heap memory. That way, you can decide how much heap memory you need at runtime (and even change how much you need dynamically), but still reference it the same way as if you were using a stack variable.
Okay, so hopefully that's clear, but then if you're not using malloc() to request heap memory, why bother using pointers at all? The answer goes back to the fact that C is such a low-level language. In higher level languages, the entire process described above is taken care of for you, and you can simply throw around more complex data types like strings and arrays as if they were plain old variables. Not so in C. In C, you can only pass a few variables at a time to a function, and each variable can only contain a single number. So what do you do if you need to operate on an array, or a string (both of which are technically the same thinga list of numbers)? Well, you pass a pointer to the beginning of the list, and tell the program that instead of interpreting the number being passed as a literal number, interpret it as a memory address. The function receiving the pointer can then go to that memory address (called dereferencing the pointer), and find the data it's supposed to be working on.
Really, a pointer is just a number, because every variable in C is just a number. The only difference is how that number is interpreted. It's entirely possible (if generally inadvisable) to interpret a pointer as an integer, and print the literal value of the pointer (as opposed to the value it points to), perform math on it, etc. It's likewise possible to dereference a variable declared as a plain old integer, and grab whatever piece of memory happens to be at that location. C is extremely permissive in this regard, and doesn't especially care what type of data is in what variable. Generally, the type is inferred from context, and the compiler will usually let you mix data types with little more than a warning (although doing so is almost always the result of a mistake, and you can avoid the warning by explicitly telling the compiler that you know what you're doing by casting the variable to a different type).
To sum up, the real reason you need to use a pointer in C is if you want to operate on any data stored in a range of memory, like an array or a string. C can only work on a single number at a time, so it doesn't really have any concept of ranges of memory. This is why trying to copy a string from one variable to another using the = operator doesn't work in C, for example.