Pointers
Learn about the purpose of pointers in C, about declaring and dereferencing them, and about the implications of the size of a pointer.
We'll cover the following
Pointers represent one of the more powerful features of the C language, but also one of the most feared. 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 that truthfully are rarely used in practice. You may have seen bizarre looking pointer syntax, (and you may see it again), but typically, pointers do not have to be used in a horribly complicated way. Typically pointers 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, directly access a block of memory, they are used a lot for strings and structs. It’s not difficult 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, how to store an address of a memory location in it, and how to 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
int
variableage
and initialize it with30
. -
On line 6, we declare a variable
p
whose type is a pointer to anint
.The star
*
syntax is how we declare the type of a variable as a pointer to a particular other 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
p
that 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 top
the 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
age
is the integer30
. - The value in the variable
p
is the address of theage
variable. - The variable to which
p
points has a value of30
.
- The value in the variable
-
On line 9, the value of the
p
pointer 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
p
points to, by using the*p
syntax. Sop
is the address of anint
variable, whereas*p
is the value of theint
variable thatp
points to. -
On line 12, we use pointer dereferencing again, but this time to set the value of the variable pointed to by
p
to40
. Sincep
points to theage
variable, we are setting the value of the address in memory corresponding to theage
variable 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 232 distinct values, i.e. 232 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 264 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.