C++ Pointers – Working with Addresses

To properly understand C++ pointers, some knowledge on Computer Systems is a must. Every byte of data on a computer, is a stored at an “address” somewhere on the computer. Just like houses have address to identify their location, so too does data.

Uptil now we have been simply storing data and accessing it without worrying about the address at which it’s stored. But what if we don’t want the data, and instead want the address at which it’s stored? And this is where C++ Pointers come in.

A Pointer is a type of variable, that can store addresses instead of data. Using this address, we can achieve a variety of different things. We can even use this address to access the data stored in it, as well as the ability to pass “by reference”.


Understanding Pointers and Addresses

The below diagram illustrates what happens when you regularly assign a value to a new variable, using a previously existing variable. Basically just copying the value from one variable to another.

C++ Pointer Variable to Variable

The Diagram shows that the value 27 was copied into the new variable. Now let’s see what happens when we create a pointer.

C++ Pointer to Address

Instead of copying the value, the Pointer stores the address of the variable in question. Unlike before, there is now a connection between the pointer and variable. The reason being that each address in the computer system is unique, allowing us to locate the variable who’s address we have stored. Using this address, we can then easily do other things like accessing the data stored in it.

The reason we can’t do this with regular variables is obvious. You only copy over the values, which are non-unique and leaves no connection between the two variables.

In a sense, we can say that a Pointer “points” to the variable whose address it has stored.


Basic Pointer Operations

Let’s begin writing some Pointer related code in C++.

int main() {
    int *ptr;
}

This code shows how to declare a pointer in C++. All you have to do is an asterisk ( * ) before the pointer name. A pointer with type int, will be able to point to int values, and a pointer for strings, will be able to point to string values and so on.

Let’s actually store an address into the pointer now. We can do this in two ways.

int main() {
    int x = 5;
    int *ptr;
    ptr = &x;
}

Or, a more short hand technique:

int main() {
    int x = 5;
    int *ptr = &x;
}

Dereferencing Pointers

If we were to just normally print out the data in the Pointer, we would just get the address value stored in it. But what if we want the value stored at the address? This is what we call Pointer De-referencing.

C++ Pointer to Variable

The above diagrams illustrates what we are trying to achieve here.

To “de-reference” a pointer, all we have to do is include the * (asterisk) next to it. This will locate the value at the said memory location, and retrieve it for us.

Let’s try printing out some data now.

#include <iostream>

using namespace std;

int main() {
    int x = 5;
    int *ptr = &x;

    cout << "Address: " << ptr << endl;
    cout << "Value: " << *ptr << endl;
}

Here is the output of the above code.

Address: 0x61fe14
Value: 5

As you can see, printing it out with the asterisk, gave the address as the output.


Passing by Value

This doesn’t actually have anything to do with Pointers. But, it’s nessacery to understand properly what Passing by Value is, before we move on to explaining Passing by Reference.

int inc(int num) {
    return num + 1;
}

int main() {
    int x;

    x = inc(3);
    cout << x << endl;
}

When passing any value or object into a function, it basically gets “copied” over. You might think that the value 3 that you passed into the function was transferred to the inc() function, but that’s wrong. What really happens requires some OOP knowledge, but basically a local duplicate is created within the inc() function. The point to remember here, is that no matter what we do with this duplicate, it will not effect the original value.

Now let’s try this again, but using pointers instead.


Passing By Reference

In pass by reference, we do not pass the value in question, rather we pass the address of that value using the &(address) operator. With this address in the function, any change we make to the value at the address will be reflected in the original variable x.

void inc(int *num) {
    *num += 1;
}


int main() {
    int x = 3;

    inc(&x);
    cout << x << endl;
}

Output:

4

What happened here, was that we incremented the value of x, inside the inc() function using it’s address. We didn’t have to return anything as the new value is already available in main. Hence, printing out the value of x, gives us 4 which is +1 more than it’s original value, 3.


Pointers and Arrays

So far we have been discussing how Pointers work with regular variables. Let’s now take a look at how they act with Arrays instead.

Arrays are contiguous blocks of memory, containing a whole bunch of different values. Since a pointer cannot point to all of them, it by default points to the first value in the Array. But now how do we access the other elements in the array? For the answer to this, look at the diagram below.

C++ Pointer to Array

Since arrays are continuous, the next data value is located right after the first. This means that if we “increment” the pointer, it will point to the next value in the array.

In the below example, we demonstrate how the value and address change as the pointer to the array is incremented.

int main() {
	int arr[5] = {1, 2, 3, 4, 5};
	int *ptr = arr;
	
	cout << *ptr << " at Address: " << ptr << endl;
	ptr++;
	cout << *ptr << " at Address: " << ptr << endl;
	ptr++;
	cout << *ptr << " at Address: " << ptr << endl;
}

Output:

1 at Address: 0x70fe00
2 at Address: 0x70fe04
3 at Address: 0x70fe08

Did you notice how the address increments by 4 each time? That’s because an int is 4 bytes, hence the next value is located 4 bytes after the first. Try doing this with doubles or characters instead and see the result.


Dynamic Pointers in C++

Another very important concept in C++ is the use of Dynamic Pointers. This is way for us to manually allocate new memory for a pointer.

Normally, data in C++ is stored on what we call the “stack”. With Dynamic Pointers, we use the new and delete keywords to allocate and deallocate memory on what we call the “heap”. The difference between the stack and the heap is a very lengthy discussion, which we will have elsewhere.

For now, the important point to remember here, is that when allocating memory to heap, it must be destroyed manually using delete. This is unlike the stack memory, which automatically allocates and deallocates memory.


Using New And Delete

In the example below, we have done three things. First we declared a pointer, then we allocated memory to it with new, and then finally deleted it at the end of the program.

int main() {
    int *ptr;

    ptr = new int;

    delete ptr;
}

The new keyword actually returns an address of a new memory location, to the pointer. The int type tells new, what the size of the assigned memory should be (4 bytes in this case). If you were to print out the value at this memory location, it would be a garbage value as we have not assigned anything to it.

Let’s take a look at another example. Here we have showcased two new things. By passing a value into the brackets of int, or any other datatype, you can assign the new memory a value. Furthermore, using new you can repeatedly assign new memory to the pointer, even after using it once.

int main() {
    int *ptr;

    ptr = new int(5);
    cout << *ptr << endl;

    ptr = new int(2);
    cout << *ptr << endl;

    delete ptr;
}

There is a lot more to Dynamic Pointers than what we have covered here. For the full story, check out our separate dedicated tutorial on Dynamic Pointers in C++.


C++ Smart Pointers

Pointers are a great asset, and can be used to solve a variety of different problems. However, Pointers (especially Dynamic Pointers) are not very easy to use, and when used for complex problems, even a slight mistake can ruin everything. Tracing the memory and addresses, ensuring that there are no dangling pointers etc, can be a rather daunting tasks. Luckily, C++ has introduced the concept of Smart Pointers to help us.

Smart Pointers are a whole separate discussion, so follow this link here to it’s separate dedicated tutorial.


This marks the end of the C++ Pointers Tutorial. Any suggestions or contributions for CodersLegacy are more than welcome. Questions regarding the tutorial content can be asked in the comments section below.

Subscribe
Notify of
guest
0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments