5 minutes
What are pointers and why you should use them
A pointer is a variable that stores or points to the address of another variable.
Pointers are one of the greatest mysteries in programming. In order to wrap your head around how they work, it is imperative to understand how memory is allocated in programs.
Once you run a program, all its variables are allocated some space in memory. The slots in memory are marked with addresses using hexadecimal numbers.
Memory Addresses
We can think of memory addresses as hotel room numbers. A hotel has many rooms. Each room has its unique number and the rooms accommodate different tourists from time to time.
It’s the same with variables in programming. The program is the hotel, the different variables(rooms) have memory addresses(room numbers) and inside the variables we have values(tourists).
int x;
int y;
int result;
By declaring three variables, the program will allocate each a slot in memory. The memory addresses of x, y and result are separated by 4 since an integer occupies 4 bytes in memory.
The Address Operator
The ampersand (&) when placed in front of a variable, returns its memory address. Here, we save the memory address of result in the variable resultPtr.
int *resultPtr = &result;
The variable resultPtr is initialized with the memory address of the result variable.
User Input
cin>>x;
cin>>y;
The application looks up variable x’s memory address which is 0xffff00
, and stores the value entered by the user into the respective memory slot. For our case let’s give the program the inputs 3 and 4.
Notice the contents of resultPtr
have not changed. It is still 0xffff08
. This is because, unlike the other variables, the content of resultPtr is the memory address of the result variable. Changing the value of a variable does not change its address.
Next, we compare the two variables - x and y - to determine which is greater than the other.
if( x > y ){
result = x;
} else {
result = y;
}
The Indirection operator.
When the dereferencing or indirection operator (*) is placed in front of a pointer variable, it returns the value of the object being pointed to.
cout<<*resultPtr;
// Output: 4
The program looks at what is stored in resultPtr which is 0xffff08. It then searches for that address in memory and returns the value stored at that particular address, in our case - 4.
This is the same as printing out the contents of result. The difference is that we are accessing the value stored in the variable result indirectly.
cout<<result;
// Output: 4
Let’s look at another example
int x = 1;
int y = 2;
int sum;
int *xPtr = &x;
sum = x + y; // sum is 3
*xPtr = 5
sum = x + y; // sum is 7
How comes the second sum is 7 and not 3?
In this case, the program looks at what is stored in xPtr
which is the address of x
. It then searches for that address in memory and assigns to it the value 5
. In doing so, we have changed the value of variable x
indirectly.
This can have unintended consequences especially when working on a large codebase. To prevent variables from having their contents altered indirectly, we can declare them with the const
keyword.
const int x = 1;
const int y = 2;
int sum;
const int *xPtr = &x;
sum = x + y; // sum is 3
*xPtr = 5
sum = x + y;
When we try to compile this code, the compiler gives us the following error.
app.cpp: In function 'int main()':
app.cpp:11: error: assignment of read-only location '*xPtr'
xPtr = 5;
^
According to the error, we are trying to change the contents of a read-only location. Using the const keyword ensures that the contents of variables can’t be changed, whether directly or indirectly.
Why use pointers?
A common use of pointers is when passing large variables to and from functions. When a variable is passed into a function, a copy of it is created and stored in memory.
Instead of passing large data types into functions directly, a pointer to the variable can be passed. This prevents the program from creating a copy of the variable.
int addition(int *a, int *b){
return *a + *b;
}
int addition(int a, int b){
// Extra 8 bytes used
return a + b;
}
In the first function, the memory addresses of the variables are passed as function arguments but the second function receives a copy of the variables thus consumes an extra 8 bytes of memory.
C/C++ is heavily used in embedded systems such as microwaves and washing machines. Resources on these devices are usually quite limited, for example, a microwave’s computer might have a ram of 1KB. With that kind of memory capacity, the program running on it must be as memory efficient as possible.
Microwave example
class CookMode{
public:
string mode_name;
float temp;
int cooktime;
};
This is a hypothetical code base for a micro-controller running on a microwave. The computer has a memory of 1KB. Microwaves have different modes and the CookMode class is used to model them.
int main(){
CookMode df;
CookMode pc;
df=CookMode("defrost",67.8,105);
pc=CookMode("popcorn",76.3,35);
// Memory usage in bytes
sizeof(defrost) + sizeof(popcorn);
}
We declare 2 different modes - defrost and popcorn.
Our simple program with 2 modes is consuming 36 bytes of data via member variables - not including the memory consumed by the program itself being loaded into memory.
Coupled with the fact that the codebase can be quite complex and involve lots of variables and functions, C/C++ makes it easier to efficiently make use of memory using pointers.
In a nutshell
Pointers are powerful in maximizing the little memory available in embedded systems. This enables programmers to build complex applications on simple hardware.