Well it has a lot of uses. First off, it's faster and more efficient because you don't have to constantly copy and allocate more memory to keep all the data in, which is especially useful when you have pieces of data that are gigantic and take up multiple megabytes of memory.
Second, it allows you to have a function that edits variables in the caller. Let's say you had one function like this (This is just generic pseudocode that uses C-style pointer syntax):
function initializeNumbers()
{
Number *a, *b;
*a = 5;
*b = 8;
}
Then how would you go about adding these numbers together and put the result in A?
Well, you could do this (Using the * operator to de-reference):
*a = *a + *b;
But if instead of adding you're doing some much more complex operation, you might want to break it up into different functions. So instead, you could do this:
function add(Number* a, Number* b)
{
*a = *a + *b;
}
function initializeNumbers()
{
Number *a, *b;
*a = 5;
*b = 8;
add(a, b);
printLine(a, b);
}
to print out 13 and 8.
Third, it lets you represent the absence of data. If you have a non-pointer object, it could consist entirely of zeros, but those zeros do not indicate an absence of data, the data IS zeros.
If you want to represent an absence of data, you just have to set the pointer to null.