Search

8.8 — Pointers

Pointers are one of C++’s historical boogeymen, and a place where many aspiring C++ learners have historically gotten stuck. However, as you’ll see shortly, pointers are nothing to be scared of. In fact, pointers function very much like references. But before we explain that further, let’s do some setup.

Consider a normal variable, like this one:

When this statement is executed by the CPU, a piece of memory from RAM will be set aside. For the sake of example, let’s say that the variable x is assigned memory location 140. Whenever the program sees the variable x in an expression or statement, it knows that it should look in memory address 140 to get the value.

The nice thing about variables is that we don’t need to worry about what specific memory address is assigned. We just refer to the variable by its given identifier, and the compiler translates this name into the appropriately assigned memory address.

The address-of operator (&)

The address-of operator (&) returns the memory address of a variable. This is pretty straightforward:

On the author’s machine, the above program printed:

5
0027FEA0

Memory addresses are typically printed as hexadecimal values (we covered hex in lesson 4.12 -- Literals).

Although the address-of operator looks just like the bitwise-and operator, you can distinguish them because the address-of operator is unary, whereas the bitwise-and operator is binary.

The dereference operator (*)

Getting the address of a variable isn’t very useful by itself.

The dereference operator (*) returns the value at a particular address:

On the author’s machine, the above program printed:

5
0027FEA0
5

Although the dereference operator looks just like the multiplication operator, you can distinguish them because the dereference operator is unary, whereas the multiplication operator is binary.

Pointers

With the address-of operator and dereference operators now added to our toolkits, we can now talk about pointers. A pointer is a variable that holds a memory address as its value.

Much like references are declared using an ampersand, pointers are declared using an asterisk:

Syntactically, C++ will accept the asterisk next to the data type, next to the variable name, or even in the middle. Note that this asterisk is part of the declaration syntax for pointers, not a use of the dereference operator.

Best practice

When defining a pointer, place the asterisk next to the type name, not the pointer’s identifier.

Warning

Although you generally should not declare multiple variables on a single line, if you do, the asterisk has to be included with each variable. It’s easy to forget to do this if you get used to attaching the asterisk to the type instead of the variable name!

Although this is sometimes used as an argument to not place the asterisk with the type name (instead attaching it to the variable), the compiler will generally warn you when you then try to use ptr2 as a pointer since the types won’t match.

Initialization and assignment for pointers

Just like normal variables, pointers are not initialized when declared. If not initialized with a value, they will contain garbage and accessing them will result in undefined behavior. Because of this, you should always initialize your pointers to a known value.

Since pointers only hold addresses, when we initialize or assign a value to a pointer, that value has to be an address. Typically, pointers are used to hold the address of another variable (which we can get using the address-of operator). We can then use the dereference operator to access the value at the address that our pointer is holding.

For example:

This prints:

5
5

Conceptually, you can think of the above snippet like this:

This is where pointers get their name from -- ptr is holding the address of x, so we say that ptr is “pointing to” x.

Author's note

A note on pointer nomenclature: “X pointer” (where X is some type) is a commonly used shorthand for “pointer to an X”. So when we say, “an integer pointer”, we really mean “a pointer to an integer”.

The type of the pointer has to match the type of the variable being pointed to:

Note that the following is also not legal:

This is because pointers hold addresses, and the integer literal 5 (which is an r-value) does not have a memory address. If you try this, the compiler will tell you it cannot convert an integer to an integer pointer.

C++ will also not allow you to directly convert literal memory addresses to a pointer:

Unlike references, a pointer can be “re-pointed” at another object.

The above prints:

6

The address-of operator returns a pointer

It’s worth noting that the address-of operator (&) doesn’t return the address of its operand as a literal. Instead, it returns a pointer containing the address of the operand, whose type is derived from the argument (e.g. taking the address of an int will return the address in an int pointer).

We can see this in the following example:

On Visual Studio, this printed:

int *

(With gcc, this prints “pi” (pointer to int) instead).

Pointers and references are similar

Consider the following program:

This program prints:

5
5
5

As you can see, pointers and references are similar in nature -- both provide a way to indirectly access another object. The primary difference is that the syntax for references is cleaner (pointer syntax tends to require explicit use of the address-of operator and dereference operator) and safer. For this reason, references should be preferred over pointers whenever possible.

Best practice

Prefer references over pointers when possible.

Under the hood, references are often implemented using pointers, where use of the reference does an implicit dereference.

// MOVE TO NEXT LESSON

Dereferencing invalid pointers will lead to undefined behavior

Pointers in C++ are inherently unsafe, and improper pointer usage is one of the best ways to crash your application.

When a pointer is dereferenced, the application attempts to go to the memory location that is stored in the pointer and retrieve the contents of memory. For security reasons, modern operating systems sandbox applications to prevent them from improperly interacting with other applications, and to protect the stability of the operating system itself. If an application tries to access a memory location not allocated to it by the operating system, the operating system will most likely shut down the application.

The following program illustrates this, and will probably crash when you run it (go ahead, try it, you won’t harm your machine):

The size of pointers

The size of a pointer is dependent upon the architecture the executable is compiled for -- a 32-bit executable uses 32-bit memory addresses -- consequently, a pointer on a 32-bit machine is 32 bits (4 bytes). With a 64-bit executable, a pointer would be 64 bits (8 bytes). Note that this is true regardless of the size of the object being pointed to:

As you can see, the size of the pointer is always the same. This is because a pointer is just a memory address, and the number of bits needed to access a memory address on a given machine is always constant.

What good are pointers?

Pointers are useful in many different cases:

// HERE

1) They are the only way you can dynamically allocate memory in C++ (covered in lesson 6.9). This is by far the most common use case for pointers.
3) They can be used to pass a large amount of data to a function in a way that doesn’t involve copying the data, which is inefficient (covered in lesson 7.4)
4) They can be used to pass a function as a parameter to another function (covered in lesson 7.8).
5) They can be used to achieve polymorphism when dealing with inheritance (covered in lesson 12.1).
6) They can be used to have one struct/class point at another struct/class, to form a chain. This is useful in some more advanced data structures, such as linked lists and trees.

So there are actually a surprising number of uses for pointers. But don’t worry if you don’t understand what most of these are yet. Now that you understand what pointers are at a basic level, we can start taking an in-depth look at the various cases in which they’re useful, which we’ll do in subsequent lessons.

Conclusion

Pointers are variables that hold a memory address. They can be dereferenced using the dereference operator (*) to retrieve the value at the address they are holding. Dereferencing a garbage pointer may crash your application.

Best practice

When declaring a pointer variable, put the asterisk next to the variable name.

Best practice

When declaring a function, put the asterisk of a pointer return value next to the type.

Quiz time

Question #1


What values does this program print? Assume a short is 2 bytes, and a 32-bit machine.

Show Solution

Question #2


What’s wrong with this snippet of code?

Show Solution


8.9 -- Pointers and const
Index
8.7 -- Pass by reference and return by reference

24 comments to 8.8 — Pointers

  • papagym177

    Unless I missed something in the context or content of this section.

    Why did they call (*) a "Dereferencing Operator"? I'd call it a "reference operator", it refers to (points to) the address of another variables location. That address does not go away (get dereferenced) until the close of the statement it's in. The closing brace (}). Did I miss something?

    Text from lesson attached below in " ".

    " int nValue = 5; int *pnPtr = &nValue; // pnPtr points to nValue *pnPtr = 7; // *pnPtr is the same as nValue, which is assigned 7 cout << nValue; // prints 7

    Pointers can also be assigned and reassigned:"

    The code above the last sentence should be moved somewhere below the Pointer sentence since it refers to the code that's written below it.
    You say pointers can be assigned and reassigned. Does that mean that they are not always local but are used for global situations as well.

    Sorry again for being your critic. We can see that you've done an outstanding job here.

  • alexlydiate

    This is a beautifully clear explanation - many thanks.

  • KILLDOZER

    Hey, Alex. I just wanted to let you know I really appreciate you not only giving away all this knowledge without asking in return, but doing so in such a well structured way. In my book, this makes you an admirable person. On a sideline, with the knowledge I've acquired thus far, I started making a text-based dungeon crawler. I've already made functions which can generate a random map, and draw it using X's & whitespaces. I'm confident that it will turn out fairly nicely, and it's all thanks to you.

  • voi

    I am working on MS visual Studio 2010. OS is win7. What I know is win7 is 64 bit OS. yet I get pointer size as 4. Why it is not 8?

    int* A;
    cout<<"\nSize of memory address is"<<sizeof(A);

    Output is:
    Size of memory address is4

    Thanks for answer.

  • Ollie999

    Also my computer has 8gb of ram so if pointers are only 32-bit for some reason then surely only half the computers memory could be used? I don't get it.

  • Ollie999

    on my computer (windows 7 64-bit) sizeof(pntr) returns 4 when I was expecting it to return 8 based on this tutorial. Does anyone know why this is?

  • capitanui

    I have a question ...i cannot understand something.
    I have this simple code :

    int *pValue;
    *pValue = 4;
    cout<<*pValue;

    It's all ok..it prints 4;

    I i declare another one my program crushes and i cannot understand why since is the same thing.

    int *pValue, *nValue;

    *pValue = 4;
    *nValue = 5;

    cout<<*pValue<<*nValue;

Leave a Reply

You can use these HTML tags

<a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code class="" title="" data-url=""> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong> <pre class="" title="" data-url=""> <span class="" title="" data-url="">