In this problem, you will explore the way in which the data associated with your code is mapped into virtual memory. You should come away with a much clearer picture of exactly what causes certain types of information to be stored in stack, heap, etc.
You're going to create numerous variables and functions, and print out the location where they are stored in virtual memory.
To make things simpler and more human-readable, we'll be representing all pointers (including function pointers) as integers; you can do this by casting them to the intptr_t type.
Note that the C++ standard does not guarantee that it is possible to cast a function pointer to an intptr_t (or to a void*, for that matter).
This is because (among other reasons) on certain platforms function pointers and object pointers might not necessarily be the same size.
Nonetheless, the POSIX standard (which largely governs implementations of Linux, among others) does guarantee that it is possible to cast a function pointer to a void* (and thus to a intptr_t):
All function pointer types shall have the same representation as the type pointer to void. Conversion of a function pointer to
void *shall not alter the representation. Avoid *value resulting from such a conversion can be converted back to the original function pointer type, using an explicit cast, without loss of information.Note: The ISO C standard does not require this, but it is required for POSIX conformance.
The bottom line is that as long as you are working within a Linux Docker container, or using one of the Linux runners on GitHub Actions, you can safely cast function pointers to intptr_t.
For example, you could create a pointer to the virtual memory location of function main by doing:
std::intptr_t main_address = reinterpret_cast<std::intptr_t>(main);
One interesting opportunity this opens up is that we can easily compute the relative location of variables in memory using normal integer arithmetic.
For example, suppose we have a global variable named reference_var.
We can print the virtual memory location of the function main, relative to reference_var, with the following code:
#include <iostream>
#include <cstdint>
int reference_var;
int main(int argc, char *argv[]) {
std::intptr_t reference_var_address = reinterpret_cast<std::intptr_t>(&reference_var);
std::intptr_t main_address = reinterpret_cast<std::intptr_t>(main);
std::cout << "Relative location of main: " << main_address - reference_var_address << std::endl;
}
Note that we don't need to use an ampersand (&) when casting a function (such as main) to an intptr_t, because when using a function name in this way, it is treated as a function pointer to that function.
We do need the ampersand when casting reference_var, because we need a pointer to reference_var (&reference_var).
A more verbose (but perhaps clearer) way you could optionally write the above code would be:
#include <iostream>
#include <cstdint>
int reference_var;
int main(int argc, char *argv[]) {
int *reference_var_ptr = &reference_var;
int (*main_ptr)(int, char**) = main;
std::intptr_t reference_var_address = reinterpret_cast<std::intptr_t>(reference_var_ptr);
std::intptr_t main_address = reinterpret_cast<std::intptr_t>(main_ptr);
std::cout << "Relative location of main: " << main_address - reference_var_address << std::endl;
}
Write a program that declares a global variable of type int (we'll assume this variable is named reference_var) for the rest of these instructions.
Whenever you see the phrase relative virtual memory address throughout the rest of this problem, that means the virtual memory address relative to the virtual memory address of reference_var.
Also define a class (we'll assume it is called MemoryTest) that includes at least the following members:
- A variable of type
int, which is not declared using thestatickeyword. For the rest of these instructions, we'll assume you named this variablenonstatic_int. - A variable of type
int, which is declared using thestatickeyword. For the rest of these instructions, we'll assume you named this variablestatic_int. - A variable of type
double*, which is not declared using thestatickeyword. For the rest of these instructions, we'll assume you named this variabledouble_ptr. In the class constructor, assigndouble_ptr = new double[10].
Have your code print each of the following:
- The relative virtual memory address of
argc. - The relative virtual memory address of
argv. - The relative virtual memory address of a variable of type
doublethat is declared in themainfunction and has automatic lifespan. - The relative virtual memory address of a variable of type
double*that has automatic lifespan. - The relative virtual memory address of a variable of type
doublethat has global scope. -
- The relative virtual memory address of an array of length
10in which the elements are of typedouble, and the elements are of automatic lifespan. - The relative virtual memory address of the first element of this array.
- The relative virtual memory address of the last element of this array.
- The relative virtual memory address of an array of length
-
- The relative virtual memory address of a
double*that points to an array of length10in which the elements are of typedouble, and the elements are of dynamic lifespan. - The relative virtual memory address of the first element of this array.
- The relative virtual memory address of the last element of this array.
- The relative virtual memory address of a
-
- The relative virtual memory address of an
std::vectorof length10in which the elements are of typedouble, and the vector has automatic lifespan. - The relative virtual memory address of the first element of this vector.
- The relative virtual memory address of the last element of this vector.
- The relative virtual memory address of an
-
- The relative virtual memory address of an instance of the
MemoryTestclass that has automatic lifespan. - The relative virtual memory address of this instance's
nonstatic_intmember. - The relative virtual memory address of this instance's
static_intmember. - The relative virtual memory address of this instance's
double_ptrmember. - The relative virtual memory address of the first
doublein the array that this instance'sdouble_ptrpoints to. - The relative virtual memory address of the last
doublein the array that this instance'sdouble_ptrpoints to.
- The relative virtual memory address of an instance of the
-
- The relative virtual memory address of an instance of the
MemoryTestclass that has dynamic lifespan. - The relative virtual memory address of this instance's
nonstatic_intmember. - The relative virtual memory address of this instance's
static_intmember. - The relative virtual memory address of this instance's
double_ptrmember. - The relative virtual memory address of the first
doublein the array that this instance'sdouble_ptrpoints to. - The relative virtual memory address of the last
doublein the array that this instance'sdouble_ptrpoints to.
- The relative virtual memory address of an instance of the
-
- The relative virtual memory address of an instance of the
MemoryTestclass that has global scope. - The relative virtual memory address of this instance's
nonstatic_intmember. - The relative virtual memory address of this instance's
static_intmember. - The relative virtual memory address of this instance's
double_ptrmember. - The relative virtual memory address of the first
doublein the array that this instance'sdouble_ptrpoints to. - The relative virtual memory address of the last
doublein the array that this instance'sdouble_ptrpoints to.
- The relative virtual memory address of an instance of the
- The relative virtual memory address of the
mainfunction. -
- The relative virtual memory address of a function (aside from
main) having global scope. - The relative virtual memory address of a variable of automatic lifespan that is declared within this function.
- The relative virtual memory address of a variable of static lifespan that is declared within this function.
- The relative virtual memory address of a function (aside from
Hint: Remember to keep in mind the difference between an instance of a class and a pointer to an instance of a class. Also note that the specific values you get will be different each time you run; for the purpose of the rest of this problem, just analyze the results from any single execution of your code.
At the end of this README.md file, list each required value from Part 2 alongside its relative virtual memory location, clearly indicating which requirement of Part 2 it satisfies.
In addition, indicate for each value which of the following regions of memory it is expected to be stored in: (1) Startup / Stack, (2) Heap, (3) Global / Static, and (4) Text.
Order the results from highest relative virtual memory location to lowest relative virtual memory location.
You aren't required to write code to do the ordering; you may do it by hand if you prefer.
For example, your answer might partially be:
| Brief Description | Requirement | Relative Virtual Memory Address | Region |
|---|---|---|---|
| argc | 1 | 46031242593288 | Startup / Stack |
| dynamic MemoryTest instance | 10.i | 10064608 | Heap |
| static variable in a function | 13.iii | -336 | Global / Static |
Place your answer here.