Memory Model Practice — Passing Copies
Exploring What Really Happens
Is all this “memory diagram” stuff what really happens in C++?
The C++ standard specifies an abstract model, not a real physical machine, and leaves many details unspecified so that they can best match a particular kind of machine where C++ runs.
But the real answer is yes. We do leave out a few details (which you'll see if you take CS 105), but the memory diagrams we're learning to draw are intended to help you understand what's happening when you run real code on a real machine.
In this part, we'll look at a small program and see what it does according to our model, but also actually run it and look at the results. Doing so will give us some practice with drawing memory diagrams, but we'll also see something of what really happens when we run programs.
The Program
We'll trace through the execution of this program. We're asking you to grab a piece of paper (or a tablet you can draw on) and make some memory diagrams.
#include <iostream>
using namespace std;
void passByVal(int v) {
cout << "passByVal's v is at address " << &v << ", and holds " << v << endl;
}
void passByRef(const int& r) {
cout << "passByRef's r is at address " << &r << ", and holds " << r << endl;
}
int main() {
int x = 23;
int y = 19;
cout << "main's x is at address " << &x << ", and holds " << x << endl;
cout << "main's y is at address " << &y << ", and holds " << y << endl;
// <--- Draw diagram here!
return 0;
}
Using a memory diagram, trace through the execution of the program up to the point where it says:
// <--- Draw diagram here!
Print out the memory addresses symbolically (e.g., s1, s2) rather than trying to simulate strange-looking hexadecimal numbers C++ would actually print.
There's a help page with all the rules for memory diagrams.
Here's a video to show how we got there…
Our hand simulation will print
main's x is at address s1, and holds 23
main's y is at address s2, and holds 19
Running the Code for Real!
But now let's confirm it by actually running the code.
- Open Example in Online GDB
- Look over the code, and then click (located in the ribbon across the top of the window).
Here's a tip: Keep this tab open! We'll use this code as a running example, and it may be a better way to refer back to the code than scrolling around on this page.
When we run the code, we see something like
main's x is at address 0x7ffefbb8cb78, and holds 23
main's y is at address 0x7ffefbb8cb74, and holds 19
(example-1 does nothing else.)
Your memory addresses probably won't be the same, but notice that they're big hexadecimal numbers. Also notice that y's address is smaller than x's address.
We can see that that s2
has a smaller address than s1
, and that the addresses take object size into account (e.g., an int
is four bytes).
Hopefully you can see why it's easier to just write s1
and s2
!
Parameters that are Copies of the Arguments
Let's look at the first function, passByVal
:
void passByVal(int v) {
cout << "passByVal's v is at address " << &v << ", and holds " << v << endl;
}
Notice that the parameter v
is declared as an int
. v
is a distinct int
variable in its own right, so when we pass in an int
as an argument, v
will hold a copy of that int.
Terminology: We say that the v
parameter is passed by value (a.k.a. passed by copy), because an entirely new value (the copy) gets stored in v
.
To explore what happens when we call passByVal
, we'll add these lines to main
, as shown:
int main() {
int x = 23;
int y = 19;
cout << "main's x is at address " << &x << ", and holds " << x << endl;
cout << "main's y is at address " << &y << ", and holds " << y << endl;
passByVal(x);
passByVal(y);
return 0;
}
Rerun the code by hand and figure out what it will say. Don't compile any code yet—the goal is to see how you're doing with drawing memory diagrams.
Our hand-execution says we should expect output like
main's x is at address s1, and holds 23
main's y is at address s2, and holds 19
passByVal's v is at address s3, and holds 23
passByVal's v is at address s3, and holds 19
I used
s3
ands4
, is that okay?No. The stack slots get reused.
When the first call to
passByVal
returns, the stack space is deallocated, so it's free to be allocated again for the next function call.Because it's the same function and thus works the same way, the new
v
variable ends up in the same spot the old one was.
Running the Code for Real!
Let's check to see if our hand simulation matches reality. Edit the online code to match the version above and run it.
When I run it, I see something like
main's x is at address 0x7fffc850fce8, and holds 23
main's y is at address 0x7fffc850fce4, and holds 19
passByVal's v is at address 0x7fffc850fcbc, and holds 23
passByVal's v is at address 0x7fffc850fcbc, and holds 19
This output is consistent with what we'd expect.
Keep the OnlineGDB tab open! The next page continues with the same example.
(When logged in, completion status appears here.)