Any ideas of simple problems that you need to use pointers for just so I can get an idea of why you need to use them?
I suppose something like a tree data structure would need to use pointers?
You are correct on the idea that tree structures can be created by using pointers. Here is a (far less) brief (than I intended) overview of some of the uses of pointers. You will have to excuse any vagueness or misunderstanding below as for the last year and a half I've been programming almost exclusively in Java. It is also almost 4am.
Linked data structures, like linked lists, and including trees, use pointers to create that kind of structure. A singly linked list might be composed of a structure that contains a pointer to the next object in the linked list and a value - the linked list terminates when an element points to nothing. You could have a doubly linked list wherein each element has a link both to the next element and to the one previous to it. Tree structures can use this - they tend to have a root node, and then every node contains some pointers to some amount of children. In a binary tree for example, each node contains exactly two children, so they have two pointers which point to each of those children.
You might wonder why you would want to use a linked list over say, an array. The reason is that it is less complex (usually this means faster) to insert an element into a linked list than it is to insert one into an array. A singly linked list might look like this:
Code:
[5 | -]--->[6 | -]--->[7 |/]
And that linked list would effectively contain the elements 5, 6 and 7:
[5|6|7]
Say we want to insert the value 8 into the array after 6.
So first we create a new array of length 4.
[ | | | ]
And then populate that array with the values from the old array until we get to 6:
[5|6| | ]
After which we insert the new value 8:
[5|6|8| ]
And then the last value from the array, 7:
[5|6|8|7]
Notice that we've had to effectively create the array all over again to complete the insertion. This is very slow, but must be done because arrays are contiguous blocks of memory.
With the linked list from above:
[5 | -]--->[6 | -]--->[7 |/]
We first create a new node containing 8:
[8 | /]
We reassign the link from 6 to 7, so that 6 now instead points to 8. This gives:
[5 | -]--->[6 | -]--->[8 | /]
[7 |/]
Finally, we need to link 8 to 7 to create the linked list:
[5 | -]--->[6 | -]--->[8 | -]--->[7 |/]
This is just a very simple example as to how pointers can be helpful in creating data structures for which some operations are more efficient than they could be with arrays. There are of course trade offs. It is quicker to access a random indexed element with an array than it is with a singly linked list, for example (I'll leave reasoning for that as an exercise to the reader here).
Speed is another reason. Imagine you have a struct which contains 200 different values - never mind why, for now, but this is just what you are stuck with. Now, say you want to pass that struct as an argument into a function. It cannot be as fast as passing a single integer value as an argument as more data has to be copied. A pointer here is helpful as you only need to copy the value of one integer, which represents a memory location (the pointer), and then you just access that memory location rather than copying all 200 values.
A third reason is the intentional creation of an alias. The idea of an alias is that you have two names for the same piece of memory - by modifying the data in one of those names, it also changes the data in the other. An application of this is that it gives you the ability to return multiple values from a function call. Aliases can also cause kind of nasty bugs if you do not keep track of them as you might expect. An example of this is that with regular assignments we get something like this:
Code:
int a = 0; //a is now zero
int b = a; //b is now 0
b = 3; //b is now 3, c is now 0
You can see that modifying the value of b in that instance only affects the value stored in b, not in a, as you would expect. However with pointers you get this:
Code:
int *x;
int *y;
*x = 2; //*x is now 2
y = x; //*y is now 2, *x is now 2
*y = 0; //*y is now 0, *x is now 0
As you can see, when you assign the pointer x to y, they point to the same memory location. x and y are aliases of some memory location which at that time happens to hold the value 2. As such, when you change the value that y points to, to 0, it changes for both x and y.
Don't take my word for it, though. I'm probably stupid and wrong about some of the things I have said here or I've missed things (although I have intentionally tried to keep talk of pointers separate from object oriented programming), so experiment with these things yourself. Programming is a practical thing, and you'll even get a better intuition for the theoretical side by experimenting with it through code.