Pointers
CS 106B: Programming Abstractions
Autumn 2020, Stanford University Computer Science Department
Lecturers: Chris Gregg and Julie Zelenski
Slide 2
Announcements
- Assignment 4 is due end-of-day today (Friday). The grace period for submission is end-of-day Sunday.
- Information about next week's midquarter assessment has been posted. If you have OAE accomodations and have not sent your OAE letter to the course staff, please do so as soon as you can.
Slide 3
Today's Goals:
- Introduction to Pointers
- What are pointers?
- Pointer Syntax
- Pointer Tips
- Pointer Practice
- Binky
- Back to classes
- The copy constructor
Slide 4
Introduction to Pointers
- The next major topic is about the idea of a pointer in C++. We need to use pointers when we create data structures like vectors and linked lists (which we will do next week!)
- Pointers are used heavily in the C language, and also in C++, though we haven't needed them yet (we briefly saw them last time when we introduced dynamic memory)
- Pointers delve under the hood of C++ to the memory system, and so we must start to become familiar with how memory works in a computer.
Slide 5
Your computer's memory system
- The memory in a computer can be thought of simply as a long row of boxes, with each box having a value in it, and an index associated with it.
- If this sounds like an array, it's because it is!
- Computer memory (particularly, Random Access Memory, or RAM) is just a giant array. The "boxes" can hold different types, but the numbers associated with each box is just a number, one after the other:
values (ints): | 7 | 2 | 8 | 3 | 14 | 99 | -6 | 3 | 45 | 11 |
index: | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
- In C++, the location of each box is the address of the box: the address can tell us where the variable is located (like a house address).
values (strings): | cat | dog | apple | tree | shoe | hand | chair | light | cup | toe |
address: | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 |
- Let's create
string
variable, calledpet
, with a value of"cat"
:
string pet = "cat";
- We can now refer to a particular value by its address. We can imagine it like this, based on the array above:
|
- Based on the diagram above, what is the address of the
pet
variable?- It is
10
. - The operating system determines the address, not you! In this case it happens to be
10
(according to our diagram), but it could be any value.
- It is
Slide 6
A pointer is just a memory address
- At this point, we have a variable, and we have said that the value has an address.
- If we store that memory address in a different variable, it is called a pointer.
string pet = "cat";
|
- Let's say we have another variable, called
petPointer
that looks like this:
|
petPointer
's value is a memory address.- What is a pointer? A memory address!
Slide 7
Introduction to Pointers
What is a pointer??
Slide 8
Introduction to Pointers
- We generally use an arrow to "point" from a pointer to the address it points to:
|
β |
|
- We really don't care about the actual memory address numbers themselves, and most often we will simply use the visual "pointer" arrow to show that a variable points to another variable, without the numbers:
|
β |
|
Slide 9
What you need to know about pointers
- Every location in memory, and therefore every variable, has an address.
- Every address corresponds to a unique location in memory.
- The computer knows the address of every variable in your program.
- Given a memory address, the computer can find out what value is stored at that location.
- While addresses are just numbers, C++ treats them as a separate type. This allows the compiler to catch cases where you accidentally assign a pointer to a numeric variable and vice versa (which is almost always an error).
Slide 10
Pointer Syntax
-
Pointer syntax can get tricky. We will not go too deep β you'll get that when you take cs107!
- Pointer Syntax #1: To declare a pointer of a particular type, use the
*
(asterisk) symbol:string* petPtr; // declare a pointer (which will hold a memory address) to a string int* agePtr; // declare a pointer to an int char* letterPtr; // declare a pointer to a char
- The type for
petPtr
is astring*
and not astring
. This is important! A pointer type is distinct from the pointee type.
Slide 11
Pointer Syntax
- Pointer Syntax #2: To get the address of another variable, use the
&
(ampersand) symbol. This is not a reference! It is the same symbol, but does not mean the same thing.string* petPtr; // declare a pointer (which will hold a memory address) to a string // at this point, petPtr's value is bogus!
- So far, here is what we have:
|
β |
|
- When we declare a pointer, its value is bogus! It doesn't have anything to point to yet, so the value isn't anything useful.
- Now, let's create a
pet
variable:string pet = "cat"; // a string variable
- Now we have:
|
β |
|
|
- In order to get
petPtr
to point topet
, we have to assign the address ofpet
topetPtr
. We do this like any other assignment, except that we use the&
symbol:petPtr = &pet; // petPtr now holds the address of pet
- Finally, we have this situation:
|
β |
|
- Notice that
petPtr
's value is 10, which is the address ofpet
. That's becausepetPtr
is a pointer, which means that its value is an address! - By the way: you almost never need to do this in CS106B! You'll use it more in CS 107, but if you find yourself using it in this class, double-check your reasons.
- Let's look at this example in Qt Creator.
Slide 12
Pointer Syntax
- Pointer Syntax #3: To get value of the variable a pointer points to, use the
*
(asterisk) character (in a different way than before!):string* petPtr; // declare a pointer to a string string pet = "cat"; // a string variable petPtr = &pet; // petPtr now holds the address of pet cout << *petPtr << endl; // prints out "cat"
- When we use
*
in this way, we say that we are dereferencing the pointer, which follows the address to its location and gets what is at that location.
|
β |
|
Slide 13
Pointer tips
- Pointer Tip #1: To ensure that we can tell if a pointer has a valid address or not, set your declared pointer to
nullptr
, which means "no valid address" (it actually is just 0 in C++).- Instead of this:
string* petPtr; // declare a pointer to a string with no defined value
- Instead of this:
|
β |
|
- We do this, instead:
string* petPtr = nullptr; // declare a pointer to a string that points to nullptr
|
β |
|
Slide 14
Pointer tips
- Pointer Tip #2: If you are unsure if your pointer holds a valid address, you should check for
nullptr
void printPetName(string* petPtr) { if (petPtr != nullptr) { cout << *petPtr << endl; // prints out the value pointed to by petPtr // if it is not nullptr } else { cout << "petPtr is not valid!" << endl; } }
Slide 15
Pointer practice
- The little boxes we draw to show the memory are so, so important to understanding what is happening. Always draw boxes when learning pointers!
int* nPtr = nullptr;
- What type does this pointer point to?
- What should we draw?
- Answers:
- The type that the pointer points to is an
int
β in other words, if we follow the pointer's value (which is an address!), we will find anint
. - We should draw the following:
|
β |
|
- Where did the
7224
come from?- The operating system determines what that value is!
- I just made up the value for this example, and you will almost never care what the address of the pointer itself is (in this class, anyway β you will care about that in CS 107!). The
7224
just tells us wherenPtr
is located, and we don't care!
Slide 16
Pointer Practice
- Let's continue the previous example:
int* nPtr = nullptr; int n = 16;
- What should we draw?
- We have two variables,
nPtr
andn
. They are not related to each other in any way yet:
- We have two variables,
|
β |
|
|
-
I made up the number
5432
because we don't know (or care) what it is. - Let's add one more line:
int* nPtr = nullptr; int n = 16; nPtr = &n;
- What should we do now?
- We update the value of
nPtr
to the address ofn
:
- We update the value of
|
β |
|
- We now say that
nPtr
points ton
.
Slide 17
Introduction to Pointers
What is a pointer??
Slide 18
Pointer Practice
- Let's look at the following code, and draw our pictures:
string* sPtr = nullptr; string s = "hello"; sPtr = &s; cout << *sPtr << endl;
- After the first two lines, this is what we have:
|
β |
|
|
- After the third line, we have:
|
β |
|
- The last line prints out:
hello
Slide 19
Pointer practice
- What is the output of the following?
string* sPtr = nullptr; string s = "hello"; cout << *sPtr << endl;
-
Answer:
- This is what we have in memory:
|
β |
|
|
- When you dereference a
nullptr
, you seg fault!
Slide 20
Pointer Practice
- You can also use the dereferencing operator to set the value of the "pointee" (the variable being pointed to):
string* sPtr = nullptr; string s = "hello"; sPtr = &s; *sPtr = "goodbye"; cout << s << endl;
- This is what we have in memory after the first three lines:
|
β |
|
- Then, the third line changes the picture to this:
|
β |
|
- And this is the output:
goodbye
Slide 21
Pointer Practice
- If you set one pointer equal to another pointer, they both point to the same variable!
string* sPtr1 = nullptr; string* sPtr2 = nullptr; string s = "hello"; sPtr1 = &s; cout << *sPtr1 << endl; sPtr2 = sPtr1; cout << *sPtr2 << endl;
- After the pointers and string are created:
|
β |
|
|
β |
|
|
- Next, we set
sPtr1
to point tos
, and print it out:
|
β |
|
|
β |
|
- Output:
hello
- The last two lines (repeated here) produce the following:
sPtr2 = sPtr1; cout << *sPtr2 << endl;
|
β |
|
|
βοΈ |
- Output:
hello
-
Notice that both pointers have the same value,
9988
, which is the address ofs
, and therefore, they both point tos
. - Let's keep going with this example, with three more lines of code:
*sPtr1 = "goodbye"; cout << *sPtr1 << endl; cout << *sPtr2 << endl;
- This code dereferences
sPtr1
, goes to the location it points to, and sets the value of that string togoodbye
. What do we have now?
|
β |
|
|
βοΈ |
- Because both pointers point to the same location, they both end up print out the same thing!
goodbye goodbye
Slide 22
Introduction to Pointers
What is a pointer??
Slide 23
More information about addresses
- Addresses are just numbers, as we have seen. However, you will often see an address listed like this:
0x7fff3889b4b4
or this:
0x602a10
- This is a base-16, hexadecimal representation. The
0x
just means "the following number is in hexadecimal notation." - The letters are used because base 16 needs 16 digits:
0 1 2 3 4 5 6 7 8 9 a b c d e f
- This is a base-16, or "hexadecimal" representation. The 0x just means "the following number is in hexadecimal."
-
The letters are used because base 16 needs 16 digits: 0 1 2 3 4 5 6 7 8 9 a b c d e f
- So, you might see the following β remember, we don't care about address values, just that they are memory locations:
|
β |
|
|
βοΈ |
Slide 24
Binky
- In 1999 (!), a certain Nick Parlante produced a little stop-animation video, called Pointer Fun with Binky
- Here it is in all its glory:
Slide 25
If we have time: Copy Constructors
- Let's take a look at a simple
Rectangle
class to demonstrate a problem that we need to be aware of. - Here is the header file,
rectangle.h
, for the program:#pragma once class Rectangle { public: Rectangle(double width = 1, double height = 1); // constructor ~Rectangle(); // destructor double area(); double perimeter(); double getHeight(); double getWidth(); private: double *height; // pointer to a double double *width; // pointer to a double };
- Here is the
rectangle.cpp
file:#include "rectangle.h" Rectangle::Rectangle(double width, double height) { // constructor this->width = new double; this->height = new double; *(this->width) = width; *(this->width) = height; } Rectangle::~Rectangle() { // destructor delete height; delete width; } double Rectangle::area() { return *width * *height; } double Rectangle::perimeter() { return 2 * *width + 2 * *height; } double Rectangle::getHeight() { return *height; } double Rectangle::getWidth() { return *width; }
- Here is the
main
function for our program:int main() { Rectangle r(3,4); cout << "Width: " << r.getWidth() << ", "; cout << "Height: " << r.getHeight() << endl; cout << "Area: " << r.area() << endl; cout << "Perimeter: " << r.perimeter() << endl; // let's make a copy: Rectangle r2 = r; // THIS CRASHES!!! return 0; }
- What happened? Why did we crash?
- We need to dig under the hood
- Let's look at what the following two lines do:
Rectangle r(3,4); Rectangle r2 = r;
- The two private variables we have are
int*
pointers,width
andheight
. Becuase we usenew
to allocate space for the data for those pointers, we don't have variables with names for the data, but the data is still in memory. This isr
:
|
β |
|
|
β |
|
- When you make an assignment of one class instance to another in C++, the default thing to do is to construct the class and simply copy over the data in the class variables. That would be
width
andheight
, which are pointers.- That means that what gets copied are two addressese
width
andheight
, which are pointers. - That means that what gets copied are two addresses. Below, we have
r
on the left, andr2
on the right:
- That means that what gets copied are two addressese
|
β |
|
β |
|
|
β |
|
β |
|
- Notice that both
r.width
andr2.width
point to the same pointee, and likewise forr.height
andr2.height
. This is bad news! Now, when the destructor gets called, both will try todelete
the value that was allocated, and this isn't allowed! The program crashes! - What do we do? We define a copy constructor that tells the compiler how to copy our class. Without it, it will simply copy the values in the class variables, which isn't what we want in this case.
- Here is the copy constructor definition in
rectangle.h
:class Rectangle { public: Rectangle(double height = 1, double width = 1); // constructor Rectangle(const Rectangle &src); // copy constructor ...
- Here is the copy constructor itself. We need to allocate new memory for the new data, then we assign the old data to our new copy. This goes into
rectangle.cpp
:Rectangle::Rectangle(const Rectangle &src) { // copy constructor width = new double; // request new memory height = new double; // copy the values *width = *src.width; *height = *src.height; }