Pointers

Pointers are a pretty big deal. They really open up what we can do with the language, including arrays, data structures, and more. Coming to terms with them is essential to understanding later parts of the course.

What are pointers?

Variables we've seen so far occupy some piece of memory, and use this memory to store a value:

int n = 13; // The number 13
char c = 'b'; // The letter 'b'
However, a pointer is slightly different. A pointer is a variable which holds a memory address. We say it points to this location, and allows us to refer to, and modify, the value stored there.

Why do we use pointers?

Pointers allow us many benefits, some of which I mentioned above. Pointers give us the foundation of arrays, allowing us to store a sequence of values. Combining this with the char type allows us to represent words and sentences as opposed to letters. It also allows us to design data structures such as a linked list; a structure of nodes with each node containing a pointer to the next node. We can also use pointers to get around C's limitation of returning a single variable from a function by giving the function a pointer to the variable/s we wish to be modified. Now these variable values can be accessed and modified from inside the function, with no need to return them!

How do we use pointers?
Introducing the asterisk

To declare a pointer we use the asterisk, *, combined with a data type to indicate what type is being pointed to.

int* intPointer;
char *charPointer;
float * floatPointer;
double*doublePointer;

You may have noticed above that the asterisk is in different positions between the type and the variable name. All of these are valid, and there can actually be any amount of whitespace (spaces, tabs, newlines) between the type, asterisk, and variable name. However, you'll usually see the first and second methods being used. I personally like the first one as it helps me read it as an "int pointer", and makes it slightly different from the dereferencing notation (which we'll get to soon). However, when declaring multiple variables on the same line, you need to include an asterisk for each variable name:

int *a, *b, *c;

Getting an address
Introducing the ampersand

Now, it's all well and good to be declaring these pointers, but we're not really assigning any value to them. This is where we bring in another piece of syntax, the ampersand, &. Normally when we use a variables name we get the value of that variable. However, we an ampersand before the variable we get the address of that variable, which we can then assign to a pointer of matching type!

int n = 20;
int* p = &n;

p is now a pointer to the memory location where n stores its value (we say p points to n). So what can we do with this pointer? Well I mentioned earlier that we can modify the memory that a pointer points to. For this we'll need to introduce another bit of syntax.

Getting a value
Introducing the... asterisk?

This is one of the more unfortunate bits of C syntax. Declaring a pointer and getting the value a pointer points to use the same symbol, *. When we 'get the value' that a pointer points to, this is called dereferencing the pointer. Let's extend the above example into a runnable program:

#include <stdio.h>

int main(){
    int n = 20;
    printf("The value of n is %d\n", n);
    
    int* p = &n;
    printf("The value of *p is %d\n", *p);
    
    *p = 30;
    printf("The value of *p is %d\n", *p);
    
    printf("The value of n is %d\n", n);
}

As before, p gets assigned the address of n. Now we dereference p (to get its value) and assign the value 30. So what's the value of n after this change? Well, remember that p points to the same memory location as n, and now we've changed the value at that location, meaning that n has changed too! Here's the output of the program:

The value of n is 20
The value of *p is 20
The value of *p is 30
The value of n is 30