Pointers
Learn about the purpose of pointers in C, how to declare and dereference them and the implications of the size of a pointer.
We'll cover the following...
Pointers represent one of the more powerful and most feared features of the C language. Some of this fear and apprehension stems from the ridiculously obtuse ways that pointers may be used in C. Often tutorials and courses on C approach teaching pointers by asking the student to decipher bizarre-looking pointer syntax combinations–many of which are rarely used in practice. You may have seen such syntax before, (and you may see it again), but typically, pointers do not have to be used in a horribly complicated way. In fact, they are pretty straightforward.
The basic idea is that a pointer is a special data type in C, that contains an address to a location in memory. Think of a pointer as an arrow that points to the location of a block of memory that in turn contains actual data of interest. It’s not unlike the concept of a phone number or a house address.
There is a code example below that illustrates this more clearly.
Purpose of pointers
Pointers allow us to work with dynamically allocated memory blocks on the heap. We can also use pointers to deal with stack variables, but in most cases this just isn’t necessary.
As pointers can be used to manually and directly access a block of memory, they are commonly used for strings and structs. It’s easy to imagine that passing a large block of memory (such as a struct that contains many things) to a function would require copying all that data on the stack. Passing the address of such a block of memory is more efficient than making a copy of it and passing in that copy, only to delete that copy when your function is done with it. Passing the address of a variable to make it accessible in a function is called passing by reference. Passing the value of a variable by allocating a new local variable and copying its value into it is called passing by value.
A working example
To use a pointer, we need to know how to declare a pointer, store the address of a memory location in it, and use it to access that memory location.
Declaring a pointer
Here is how to declare a pointer and store an address in it:
#include <stdio.h>int main(void){int age = 30;int *p;p = &age;printf("age = %d\n", age);printf("p = %p\n", p);printf("*p = %d\n", *p);printf("sizeof(p) = %ld\n", sizeof(p));*p = 40;printf("*p = %d\n", *p);printf("age = %d\n", age);return 0;}
Let’s first run through lines 5–7 visually before seeing an explanation of how the pointers are declared and used in the code.
-
On line 5, we declare an
intvariableageand initialize it with30. -
On line 6, we declare a variable
pwhose type is a pointer to anint.The star
*syntax is how we declare the type of a variable as a pointer to a particular type. Usually, we need to declare pointers as pointing to a particular type, but the only exception is a so-called void pointer, which is a pointer to an unknown type. Don’t worry about void pointers for now.At this point, we have a variable
pthat is a pointer to anint, but it doesn’t point anywhere. We have only declared that we want a space in memory for the pointer.
-
On line 7 is where we assign a value to our pointer
p. We assign topthe address (the location) of the other variableage.When the
&operator is used in C before a variable name, the memory address of that variable is retrieved.So now at this point in the program, we have the following values stored in the variables (which we can see in the output):
- The value in the variable
ageis the integer30. - The value in the variable
pis the address of theagevariable. - The variable to which
ppoints has a value of30.
- The value in the variable
-
On line 9, the value of the
ppointer is printed out. When the program is run, the output can be seen as a long strange looking string of letters and numbers. This is in fact a hexadecimal memory address.
Dereferencing a pointer
Accessing the value that a pointer points to is called dereferencing a pointer. This can be done by using a star * followed by the pointer name—outside of a declaration. For example, in the code shown above:
-
On line 10, we see how to access the value that the pointer
ppoints to, by using the*psyntax. Sopis the address of anintvariable, whereas*pis the value of theintvariable thatppoints to. -
On line 12, we use pointer dereferencing again, but this time to set the value of the variable pointed to by
pto40. Sinceppoints to theagevariable, we are setting the value of the address in memory corresponding to theagevariable to40.
Size of a pointer
From line 11 in the above code and the corresponding output, we can see that our pointer p is 8 bytes.
Remember, there are 8 bits in a byte, so a 4-byte pointer can hold up to 32 bits, or 2<sup>32</sup> distinct values, i.e. 2<sup>32</sup> distinct addresses in memory. So 32-bit pointers allow us to access up to 4 gigabytes (4 GB) of memory. If we want to access more than 4 GB of memory, we’ll need bigger pointers! On 64-bit systems, pointers are 8 bytes, which allows for 2<sup>64</sup> distinct addresses in memory. It will be a long time before our computer has that much RAM (16 exabytes in principle, which is 1 billion gigabytes or 1 million terabytes).
Another powerful use of pointers is that they allow us to communicate with arrays.