How to Dereference Pointers in C++
C++ is one of the most renowned programming languages of all time. It offers exceptional features, performance, and control that is unparalleled by most languages. However, C++ does not offer memory safety which gives a lot of control but gives a very big gun to shot yourself in the foot or blow your foot off!
One of the most powerful yet hazardous features of C++ is pointers. And more specifically, pointer operations.
One of the most common pointer operations is pointer dereferencing. However, like most pointer operations, if not handled carefully, it can lead to serious problems like segmentation faults or undefined behavior.
These issues usually occur when you try to dereference a null or uninitialized pointer, or a pointer that points to a memory that has already been deallocated.
The goal of this tutorial is to explore the various methods you can use to dereference a pointer in C++. We will also explore some safety measures you can take in pointer dereferencing to ensure you stay safe when working with pointers.
Understanding Pointers
Before we dive into the ways of dereferencing pointers, its good to understand what a pointer is and how it functions.
A pointer refers to a variable that stores the memory address of another variable. We can declare a pointer using the &
operator.
int x = 10;
int* ptr = &x;
In this case, the variable ptr
is a pointer to the variable x
.
What is Pointer Dereferencing?
We keep mentioning pointer dereferencing but what exactly is it? In simple terms, pointer dereferencing is the process of accessing the values stored in a given memory location as pointed by the pointer variable.
Once we have dereferenced a pointer we can manipulate or retrieve the data that resides at the memory address in any safe manner.
How to Dereference a Pointer
In C++, we can dereference a pointer using the dereference operator *
.
For example, to dereference the variable ptr
from the previous example, we can use the code as shown
cout << *ptr << endl;
Safe Dereferencing of Pointers
Pointer dereferencing can result in unexpected behavior or some common memory related bugs such as segmentation faults, etc.
The following are some key points when it comes to pointer dereferencing in C++
Never Dereference a Null Pointer
A null pointer refers to any pointer variable that does point to any memory address. Never dereference a NULL pointer as doing so can lead to unexpected behavior.
Take for example the code shown below.
...
int* ptr = nullptr;
sd::cout << *ptr << std::endl;
...
In the example above, we define the pointer variable called ptr
which points to an invalid address. This means the pointer is a null pointer. However, if we dereference the pointer, it will return a seg fault as shown:
$ ./a.outs
Segmentation fault
Therefore, before dereferencing a pointer, ensure it’s not null as shown in the example below:
#include <iostream>
int main()
{
int *ptr = nullptr;
if (ptr)
{ // is it null
std::cout << *ptr << std::endl;
}
}
In this case, the code will only dereference the pointer if its not null which ensures memory safety.
Always Initialize Pointers
Another technique of ensuring safe pointer dereferencing is always initializing pointers.
An uninitialized pointer in C++ might hold a garbage value, and thus might not point to a valid memory address. To solve for this, “Always initialize pointers during declaration”
int* ptr = nullptr;
Never Dereference Freed Memory (Dangling Pointers)
There are situations called dangling pointers. A dangling pointer is a pointer that is points to a memory address that has been deallocated or freed. Dangling pointers are always safe to dereference.
#include <iostream>
int main()
{
int *ptr = new int(10);
delete ptr; // dangling pointer
std::cout << *ptr << std::endl;
}
If we run the code above multiple times, you will notice it returns different values each time.
$ g++ dereferencing.cpp
$ ./a.out
736591632
$ ./a.out
-752910576
Hence, before using a pointer, ensure that it does not point to freed memory.
Always Validate Any Pointer Arithmetic
Ah, pointer arithmetic!!!
Pointer arithmetic refers to any operation that changes the memory address a pointer is referring to. Although it is a very good feature, it is a pretty dangerous tool if used unsafely.
One way to avoid issues with pointer arithmetic is to ensure the resulting address is valid and within the boundaries of the allocated memory block.
Performing pointer operations that outside the allocated memory block can lead to undefined behavior as demonstrated in the example below:
#include <iostream>
int main()
{
int arr[10];
int *ptr = arr;
ptr += 15; // points outside the array
std::cout << *ptr << std::endl;
}
Output:
32759
You can ensure the pointer arithmetic results in a valid memory address as shown
int main()
{
int arr[10];
int *ptr = arr;
ptr += 5; // no crossing memory block
if (ptr >= arr && ptr < arr + 10)
{ // check ptr validity
std::cout << *ptr << std::endl;
}
}
Resulting output:
0
And there you have it.
Conclusion
Pointers are a powerful tool in C++, but they can also lead to hard-to-diagnose bugs if not handled properly. By following some safe tips provided in this post, you can help keep your programs bug-free.