C++ - C++ deference - Programming Languages

C++ Dereferencing Explained

Variables hold values. A pointer variable holds the address (the location) of another variable. Dereferencing is getting at the pointed value. Pointer variables are useful for walking the contents of a linked list data structure, using a dynamic jump table to subroutines, and passing arguments by address (so only an address is passed) rather than by value (where the entire data structure is copied). in C++ dereferencing looks like:

int clownsInCar = 25, *pointerToClowns;

pointerToClowns = &clownsInCar;
cout << "Current number of clowns is " << *pointerToClowns << ".";

Dereferencing in C++ has the potential to  be confusing, especially since it uses the * operator in two different ways. In this article, we’ll touch on the importance of memory locations and how to access them using pointers. We’ll then dive into dereferencing to better understand memory locations and see what values are held there.

Computer Memory and C++

To set the context for dereferencing in C++, let’s quickly cover the principles behind computer memory.

Computer memory can, in many ways, be thought of as a set of pigeonholes. The memory is subdivided into individual locations, and every location has its own address. The computer’s central processing unit (CPU) can read or write each memory location by its address.

Image: Pigeons in holes. Source: Wikimedia Commons

In a C++ program, we don’t always work with memory addresses directly. The C++ compiler takes care of most memory-related work when you define variables, create arrays, and use data types built into the C++ language (like integers or characters).

When diving deeper into C++ programming and creating more complex programs, we sometimes explicitly reserve sections of memory for the program’s needs. Placing the address of this memory into a pointer variable and using dereferencing to access it is one of the key techniques of advanced C++ programming.

What Is a Pointer in C++?

A pointer, again, is a variable that holds the address of another variable. In our pigeonhole example, we can point to a particular hole, for example, and in that way refer to a particular pigeon. A C++ pointer works in a similar way.

Pointers need to have the same data type of the variable whose memory address they’re storing. This means that pointers need to be declared as the same data type before they can be used. (In the example above both ClownsInCar and pointerToClowns are declared as integer type variables.

The unary operator * is used to declare a pointer and the unary operator & is used to dereference the pointer.. In both cases, the operator is “unary” because it acts upon a single operand to produce a new value.

A complete example looks like:

#include <iostream>

using namespace std;

int main()
{
    int *clownsInCar = 25, *pointerToClowns;
    pointerToClowns = &clownsInCar;
    cout << "The current number of clowns is " << *pointerToClowns << "." << endl;
    cout << "The address of the variable is " << pointerToClowns << ".";
    return 0;
}

By adding the * operator to our declaration of pointerToClowns, we identify that this variable is a pointer. Like all other variables, we can name this pointer anything that fits within a proper naming convention of a variable.

The program outputs the current number of 25 by dereferencing — *pointerToClows — and the memory address of the variable pointerToClowns. The output looks like this:

The current number of clowns is 25.
The address of the variable is 0x7ffdea8715b4.

Examples of Dereferencing in C++

We’ve identified how to use pointers in C++ to obtain memory locations, and we’ve looked at how to take advantage of the dereference operator (*) to tell us the value of the variable at the location we’re pointing to with our pointer.

Let’s work through a complete example of dereferencing in C++. Have a look at the following program:

#include <iostream>

using namespace std;

int main()
{
    int *pointer, num = 10;
    pointer = &num;
    cout << "The address of num is " << pointer << endl;
    cout << "The value of num as dereferenced by the pointer is " << *pointer;

    return 0;
}

By calling upon the dereference operator, we’re able to output the value of num by referencing the location:

The address of num is 0x7ffdea8715b4
The value of num as dereferenced by the pointer is 10

If we change the value of the pointer itself, we’ll point the pointer’s variable somewhere else. This change will not affect the value of the variable that the pointer was pointing to.

Let’s take a look at an example:

#include <iostream>

using namespace std;

int main()
{
    int *pointer, num = 10, num2 = 20;
    pointer = &num;
    cout << "The address of num is " << pointer << endl;
    cout << "The value of num as dereferenced by the pointer is " << *pointer << endl;
    
    pointer = &num2;
    cout << "Pointer is now pointing at " << pointer << endl;
    cout << "The value at pointer's new memory location is " << *pointer << endl;
    cout << "The value of num is still " << num;

    return 0;
}

We get the following output:

The address of num is 0x7ffcff094f30
The value of num as dereferenced by the pointer is 10
Pointer is now pointing at 0x7ffcff094f34
The value at pointer's new memory location is 20
The value of num is still 10

However, if we assign a new value at the location the pointer is referencing, we’ll see a change in the variable we’re referencing:

#include <iostream>

using namespace std;

int main()
{
    int *pointer, num = 10;
    pointer = &num;
    cout << "The address of num is " << pointer << endl;
    cout << "The value of num as dereferenced by the pointer is " << *pointer << endl;
    
    *pointer = 30;
    cout << "The value at pointer's memory location is " << *pointer << endl;
    cout << "The value of num is now " << num;

    return 0;
}

Using the dereference operator, we tell the program to change the value at the pointer’s location. We see the following output:

The address of num is 0x7ffdfeccfe44
The value of num as dereferenced by the pointer is 10
The value at pointer's memory location is 30
The value of num is now 30

 

The * Operator in Pointer Declaration vs. Dereferencing

The * operator can certainly make pointers and dereferencing confusing as there are two entirely different uses for the operator.

The first time we’ll see the operator in code is when we declare a specific variable as a pointer. Since we have to assign a data type to our pointers, we use the operator at that time.

Once a variable is declared as a pointer, we no longer need to use the * operator when referring to the pointer in our code. The program will remember that the variable is declared as such. For the sake of code readability, it’s highly advised to name a pointer something that leaves no doubt as to its being a pointer.

But when using dereferencing, we need to call upon the operator in a different way. With dereferencing, we need to use the operator whenever we want to reference the value of the variable that the pointer is pointing to.

Dereferencing With Complex Data Types

Complex data types in C++ such as strings and arrays can add another layer of complexity to dereferencing. Let’s take a look at each.

Arrays

When it comes to arrays, each value in an array is assigned its own memory location. The memory for the entire array is allocated side-by-side. So if we know the array’s start address and the size of elements stored in the array, we can calculate the address of any specific element by using what we’ve learned about pointers and dereferencing.

Let’s look at the code below:

#include <iostream>

using namespace std;
int main()
{
    int ar[4]{1,2,3,4};
    int *ptr = &ar[0];
    cout << "The values of our array are ";
    for(*ptr < sizeof(ar); *ptr; ++ptr) {
      cout << *ptr <<" ";
    }

    return 0;
}

After initializing an integer array, we can declare a pointer that points to one specific location in the array. We can then use a for loop to walk through the array and use dereference to provide the value at each location.

Strings

Strings are essentially arrays of characters, but a string is a data type of its own in C++. Accordingly, it’s possible to declare a string and also assign a pointer to the memory location of that string.

Let’s see what happens when we point at the memory location of a string:

#include <iostream>

using namespace std;

int main()
{
    string greet = "Hello everyone, how are you all doing today?";
    string *ptr = &greet;
    cout << *ptr;

    return 0;
}

When we initialize a string and use ptr to point to it, we can dereference ptr to see what the value is at that memory location. Interestingly, even though the string is stored in different memory locations, it outputs the entire string. This is due to the fact that the cout function processes from the character we’re pointing at until it reaches a place in memory with a string terminator!

Hello everyone, how are you all doing today?

Conclusion

As we’ve seen in this article, pointers point us to a memory location and dereference shows us the value at that location. These two functions make different use of the unary (*) operator to accomplish those feats. Dereference allows us to look at or even change values at specific locations without having to carry those values with us to different memory locations throughout our code.

Learn C++ With Udacity

Congratulations! You’re well on your way to mastering dereferencing, an advanced C++ topic that causes trouble for even seasoned programmers. Yet, there’s much more for you to learn about the world of C++.   

Udacity’s hands-on C++ Nanodegree prepares you for an exciting career as a C++ developer by guiding you through five real-world projects.

Enroll in our C++ Nanodegree program today!