The top-level answer to this is that pointers are sometimes the only way to express a computation and more often they simply lead to more compact and efficient code. As answers go, that’s not very enlightening though so here are a few concrete examples.
- To allow changing the callers data. C passes parameters by value – it makes local copy variables of parameters passed into functions. Passing in a pointer means you get a copy of the pointer but that copy is still pointing to the original memory. Pointers are a way to bypass C’s ‘pass by value’ mechanism. You could use global variables to allow access from both the caller and the callee but global variables are considered bad. Sometimes I want changes I make to be reflected in the source of the data so I need to use reference semantics through pointers. Sometimes I want to manipulate data independently of the original data so I use copy or value semantics. The difference may appear subtle but it is important – a classic and frequent problem encountered by newbies is “Why does my function not update my variable?”
- To reduce copying. Using return values allows changes to data that has been passed into a function but that requires pushing and popping data onto and off the stack. For a large array for example, instead of saying “here’s the contents of the array”, pointers allow you to say, “here’s where you can find the array already in memory”.
- To use memory efficiently both in terms of execution speed and memory usage. For example, nodes in a linked list point to each other. Deleting an element in an array means moving all the ‘higher up’ elements down by one to fill the gap. Deleting an element from a linked list simply involves changing one pointer.
- Dynamic memory allocation. If you don’t know how much you’ll need at compile time or if the stack isn’t big enough you can grab chunks from the heap at run time. You can’t do this using arrays because they have to have their size defined at compile time.
- Untyped things. e.g.
malloc
,free
,memset
,memcpy
etc just deal with memory rather than typed objects. - Pointers can hide data and implementation in
struct
s by passing pointers to opaquestruct
tags. e.g.FILE *
So if pointers are so great, why not only use pointers? Because they’re risky – you have to manage memory yourself. The object has to exist somewhere for a pointer to have something to point to. Also they’re often an unnecessary overhead e.g. for(i=0; i<10; i++)
would be overly verbose if written using pointer notation.