Pointers on Pointers

Jack Overby
4 min readOct 25, 2020

One of the best ways to understand an idea is to attempt to explain it to someone else. The process of breaking a concept down and presenting it in a simple fashion is a great way to delve into the details and improve one’s own understanding. As the Internet pretends Albert Einstein said: “If you can’t explain it simply you don’t understand it well enough”. If I’m not mistaken, Abraham Lincoln is the true originator of this fake quote, but regardless of its genesis, it’s a solid rule of thumb! This is how I feel about pointers, a vital computer science concept well familiar to anyone who has programmed in core object-oriented languages like C or C++. As a Flatiron graduate, I delved deep into JavaScript and Ruby, which are higher-level languages that “abstract away” many of the more complicated elements of programming that lie underneath. While I understand the basic idea of pointers- variables that refer to a variable’s address- I had a tough time actually using them or seeing their usefulness. That is the inspiration of this article: to dive into pointers, and using my newfound comprehension to help my fellow neophytes understand this critical programming concept.

Basic Idea

Data on a computer is stored in physical locations in memory. Such address is represented in hexadecimal form. When we declare a standard variable, we can access both its value and its address, via the ‘&’ unary symbol:

# Declaration of integer myInt, initialized to 0
int myInt{0};
# Outputting the value of the variable (myInt) and the address (&myInt)
cout<<"myInt (value, address) = ("<<myInt<<", "<<&myInt<<")"<<endl;

This outputs the following to the screen:

myInt (value, address) = (0, 0x7ffd11f65534)

The value, as expected, is 0. The address is a seemingly random hexadecimal value. Note: the ‘0x’ at the start is not part of the address value; it simply indicates that the value is a hexadecimal number rather than a string.

Now, we can declare an integer pointer that refers, not to the value of the myInt variable, but rather its address:

# Declaration of uninitialized pointer
int* myPtr{};
# Setting myPtr's value to myInt's address
myPtr = &myInt;
# Outputting to screen
cout<<"(myInt, &myInt) = ("<<myInt<<", "<<&myInt<<")"<<endl;
cout<<"(*myPtr, myPtr) = ("<<*myPtr<<", "<<myPtr<<")"<<endl;

Which gives us the following:

(myInt, &myInt) = (0, 0x7ffd11f65534)
(*myPtr, myPtr) = (0, 0x7ffd11f65534)

As we see, the pointer’s value (*myPtr) equals myInt’s value, and the pointer’s address (&myPtr) equals myInt’s address.

Modifying Pointers

Now, if we change the value of myPtr (i.e. *myPtr) from 0 to 1, we would expect the address to remain the same. However, since we’re not directly modifying myInt, we’d expect myInt to still = 0, right?

*myPt = 1;
cout<<"Now, *myPtr = 1..."<<endl;
cout<<"myInt (value, address) = ("<<myInt<<", "<<&myInt<<")"<<endl;
cout<<"myPtr (value, address) = ("<<*myPtr<<", "<<myPtr<<")"<<endl;

Perhaps surprisingly, myInt’s value changes as well!

Initially, myInt = 0...
(myInt, &myInt) = (0, 0x7fff10f6fa34)
(*myPtr, myPtr) = (0, 0x7fff10f6fa34)
Now, let's set myPtr's value to 1...
(myInt, &myInt) = (1, 0x7fff10f6fa34)
(*myPtr, myPtr) = (1, 0x7fff10f6fa34)

So modifying the pointer also modifies the original variable to which the pointer… points.
Note: notice how the hexadecimal address keep changing. This is due to the “random” way in which the computer allocates short-term memory- RAM, Random Access Memory.

The * and & operators act almost as inverse functions. The * is used to go from address to value; i.e. to get the value stored at a given value. The & goes from value to address; i.e. to find the address of a variable with a given value.

Arrays

One of the main applications of pointers in C++ is arrays. Arrays are essentially “lists” of data- objects that hold a given number of objects, e.g. chars, floats, ints. In C++, all members of an array must be of the same type: a char array can hold only chars, an int array only ints, etc.

Let’s make a simple integer array, of length 5, with values 1,2,3,4,5. Then, we’ll print out the array. Intuitively, we’d expect the elements of the array to appear on screen… right?

int intArray[5]{1,2,3,4,5};
cout<<intArray<<endl;

Here’s what we get:

Printing intArray...
0x7fffd9885ef0

Wow! Not only do we not get 5 values, we only get 1. And it’s not even a value, but rather an address! What gives?

In the words of CPlusPlus.com: “The concept of arrays is related to that of pointers. In fact, arrays work very much like pointers to their first elements, and, actually, an array can always be implicitly converted to the pointer of the proper type.”

So intArray = &intArray[0] (i.e. the first element of intArray).

One of the features of arrays is that the elements are stored next to each other in memory. Let’s see the address of the elements:

int intArray[5]{1,2,3,4,5};
for (int i = 0; i < 5; i++) {
cout<<"Element #"<<i<<": (value, address) = ("<<intArray[i]<<", "<<&intArray[i]<<")"<<endl;
}

The results are the values and addresses:

Element #0: (value, address) = (1, 0x7ffce07ed900)
Element #1: (value, address) = (2, 0x7ffce07ed904)
Element #2: (value, address) = (3, 0x7ffce07ed908)
Element #3: (value, address) = (4, 0x7ffce07ed90c)
Element #4: (value, address) = (5, 0x7ffce07ed910)

Each additional element’s address is 4 higher than the previous element. This is because an integer in C++ occupies 4 bytes, and each individual space in memory equals 1 byte.

Conclusion

This concludes my brief foray into pointers (and arrays). I myself feel a little more comfortable with pointers than I was at the start of this article. I hope you, Dear Reader, can stay the same. Ciao for now!

--

--