In C++, programmers like us have control over the memory and the lifecycle of every single variable we create. By default, every variable we've created so far has been stored in something called stack memory. Let's explore what that means. Every variable in C++ always has four things. Every variable always has a name, a type, a value and a location in memory or what we might refer to as its memory address. Notice that in this example below, we have an integer with the name prime number and its value is seven. So, here, the type is int, the name is prime number and the value is seven. The memory address is not known, but we can explore where this is going to be stored based on how we created this variable. In C++, we can actually get at where the memory is stored by using the ampersand operator, and that's going to return the memory address of a variable. Let's see how this actually works. In this program, we see that we have our main function, and we know our main function is going to be the first thing that runs. Here, we define a new variable, an integer that has a type event, a name of num and a value of seven. So, we know somewhere in memory, we have enough space to store an integer and this memory is going to contain the value seven. The first thing we're going to print out to the console is the word value, and then print out the value of the number, and that's seven. The next thing we're going to print out is the address, and the address of the variable is going to be printed out by using ampersand numbers. So, we've asking the C++ language to give us the address where that memory is being stored. Because we're going to learn that this is a stack memory address, we're going to expect this address to be a somewhat large number. So, this number might be ffff27242. Notice that this number has a whole bunch of digits to it. It's written in hexadecimal, and it's going to tells exactly the address of the variable stored in memory. The address is going to differ depending on what system you're on. So, when I run this program, I'm going to see a large number, but the exact large number is going to be different than the large number that you're going to see when you run this code. Let's go ahead and run this program and make sure that we see that this variable does have a value of seven and see that the address that we find is indeed a large value. So, let's head over to the console. Navigating into this cpp memory folder. I'm going to run make to go ahead and compile the code, and the program that we just looked at was the address of program. When we run address of program, we see that the first thing we print out is value seven, exactly what we expect, and then the address is a very large number. Here, on this system, it's 7fffc59ca388. So, given the length of this number, that's 12 or 13 hexadecimal digits long, we know that this is a very high memory address, which is exactly what we expect of stack memory. Let's look at why this is a high memory address and understand exactly how the memory works in our computer. As mentioned, stack memory is the default memory for all variables in C++ to be placed. So, we haven't done anything special. We know our variable is going to be in stack memory. Stack memory is a specifically associated with the current function, and the lifecycle of this memory is the lifecycle of the function. So, if you clear an integer inside of the function, that integer exists as long as that function is still running. When the function finally ends or the function finally returns, then the stack memory for that function is going to be released back to the system. So, lifecycle of any variable created inside of a function is exactly as long as the function is being ran. One important property to remember about stack memory is that stack memory always starts from high addresses and then grows down towards a low addresses. So, we're going to start at the highest spot in our computer's memory and start adding variables up at the high memory, growing down towards zero. So, as we saw in the previous example, we start with a huge address and we're going to keep moving down and down and down the memory addresses, which may seem a little bit backwards, but system designers chose to start stack memory high addresses and grow down towards zero. Let's see this example happen when we have two different functions. So, the very first function we have is the same main we had in our previous program. We declare an integer, named num and its value is seven. If we go ahead and draw the memory layout diagram, we know the first thing created in our memory is going to be the variable num, that has the value of seven. This is going to have a very, very high address because it's in stack memory. After we've added num to the stack, while main is still running, we're going to jump into the function foo; and a function foo is also going to live in stack memory, and here, in this foo function, we're going to grow down towards zero. So, the next address we see is going to be the address of x. So, somewhere after the address of number is going to be the address of x, which has the value 42. So, what we expect from the console is we expect the first output to say that num is equal to 7. We expect the address of num to be a large value. The third thing we expect to see is that x is going to have the value of 42, and the last thing we expect to see is the address of x is going to be a large number just like the memory address of num, except for it's going to be slightly smaller, slightly close to zero because we see that foo is below where main was running. So, main is running, it allocates the memory for num. After num has been allocated, foo is called. So, foo's allocation has to happen below the allocation for the main function. We'll notice that it's going to skip a little bit of memory, and that's just the overhead to call the foo function and some C++ internal magnets. The big ideas we expect num to have a higher memory address because it's allocated first, then the memory address of x. Let's go ahead and run this program and see if our understanding, our hypothesis actually checkout. Here, looking at our console again, we've already compiled our code, and now I'm going to go ahead and run the program foo. In foo's output, we're going to analyze it and see that the first thing that happens is num's value is seven and its address is a large number ending with 908, then x's value is 42. The memory address of x is the same large number but ending in 8bc. So, nine is a larger number than eight. So, we see that the memory where num is stored is 908, the memory where foo is stored is 8bc. 908 is a larger number than 8bc. These numbers both in hexadecimal. So, that means that everything about our understanding of how this works make sense, and you see an example right here. The memory address of num is larger because it's been allocated first. X being allocated second has a slightly smaller value. So, we saw this actually happening in real code, and I encourage you to run this program yourself and see how the memory address in your computer, they're going to be different because the layout of the memory is going to be slightly different as randomized; but you're going to see that num will always have a memory address higher because num was allocated first in our program. Awesome. Let's go ahead and talk about how we can actually use these memory addresses inside C++ to help us keep track of all of this memory. One key idea in C++ that makes C++ so powerful is the idea of a pointer. All the pointer is is, it is a variable that stores the memory address of data. I like to think of pointers as simply an indirection away from our data. So, if our data is here, 42, and maybe that's stored as an integer, a pointer is basically going to be a level of indirection away from data. We follow the pointer to where it's pointing to, and we notice that pointer is now pointing to the memory that's storing 42. Pointer itself does not store 42, but the pointer stores the memory address of 42, and we follow the contents of the pointer, we get term memory address. Pointers are so fundamental to C++ that the addition of a star to the type of the variable denotes that as a pointer type. Notice here, we have an int star as the type. So, the type of the variable is int star, the name is p and the value of what's stored in the integer pointer is going to be the address of the variable number. So, we know that there's a type, a name and a value, and this integer pointer because it's being declared normally is going to be on our stack. So, now, we have an integer pointer that stores the value of where num is actually stored in memory. The very last thing we want to mention is often we're going to have these pointers and we don't actually want to know what the contents of the pointer is, we want to know what the contents of the thing it's pointing to is, and C++ provides us a dereference operator. So, given a pointer, a level of indirection can be removed by preceding the variable with an asterisk. So, notice this gets a little bit confusing because if you append the type of the variable with an asterisk, it becomes a pointer type. When you actually use the variable, if you prepend it with an asterisk, then that variable will remove the level of indirection. You'll dereference the memory address and get the actual value. So, looking at our quick sample code before we look at a full program, here we have integer num seven. So, we have a piece of memory, that's name is num. It has the value of seven, then we have the variable p, which is a whole another chunk of memory. It is a pointer and it points to the address of num. The value in num is a third variable, and this value_in_num is going to take on the dereference value of p. So, we're going to look at p and once we see what p is pointing to, then we can go ahead and copy that value seven into value_in_num. Notice that the last line here, we do something different. So, now that we've copied num into value_in_num, we say the dereference value of P shouldn't be made equal to 42. So, we look at p, we follow that to where it's being dereferenced and we change that value from 7-42. The final state of this program, we have num that contains a value of 42, p that points the memory address of num, and value in num is going to have the value of seven. One thing I think is really useful is to really look at how all of this actually works in memory and see if we can make sense of it, and as we do it, we're going to uncover a little secret in this puzzle that we see. So, in this program, we've got are puzzle.cpp, which you also have in the code associated with this lecture and we see that we include Cube.h. We're using uiuc ::Cube, the standard include file plus the using statement. Now, we're going to go ahead and look at the main program and start running this program as though we're a computer. The first thing we do is, we create a cube pointer c. That we're going to put on the main stack. So, the first thing that happens, is we're going to allocate an area of this memory for main, so we'll call this main stack. Inside of main stack, we see the first thing we have is a cube pointers. So, this the type of this memory is going to be a cube star, the name of it is c, and it can take on the value of CreateUnitCube. So CreateUnitCube is a function, this function returns a cube star, so here we have a Cube cube. So, let's go ahead and create a CreateUnitCube stack frame, this create unit cube stack frame all the memory associated with CreateUnitCubes going to go down here, further down in memory and we create a Cube called cube, the type is cube, the variable name is cube and this is a cube, we said it's linked to be 15 and just draw a 15 millimeter cube denoted 15 and then we return a memory address to the cube. So, that means CreateUnitCube returned value is cube and that means that c points to the stack memory. So, I'm going to redraw this to clean it up just slightly, and we're going to see exactly the same thing with a pointer from main's memory into cube's memory. At this point, we have the exact same drawing we drew earlier and we have main's stack frame we have CreateUnitCube's stack frame and we see that the cube is here in CreateUnitCube and we have a pointer that points to that cube. One thing you'll remember is we said that the stack frame exists only so long as the function is running, so here on line 16, we return the memory address of cube, but because we've returned from that function, we see that this memory is no longer allocated to us. It's not our memory anymore. So, I remove the orange boxes to say that memory has been deallocated by the system, it may not be reused yet, we don't know what's happening in that memory, it may still be a cube, it may not be a cube. But if we run some other function, the operating system almost certainly is going to use the memory that we were just using for CreateUnitCube and that other function, overriding that with some other data. So, what we expecting this puzzle is that when we run someOtherFunction, that something crazy might hang to our memory and our memory is not going to be exactly what we think it should be. If everything runs as defined, we expect that the surface area of this cube is going to be six times 15 squared and the volume is going to be 15 cubed. So, they're going to be nice whole numbers, don't have any decimal points because all we're doing is squaring and cubing 15 and we expect those numbers to 15 cube to be in the range of a couple of 1,000, six times 225 is going to be in the range of about 1,500. We expect when we print out data for the surface area to be 1,500, the volume to be a few 1000 and we can run this puzzle.cpp and see exactly what happens. We still have all of the old code we ran earlier and we've already made it, so we can actually see that the compilers already trying to warn us about something that's happening. So, the compiler knows that in our puzzle.cpp, the address of our stack memory associate with local variables is being returned. That means we're already doing something we shouldn't do and the compiler is helping us out by warning us, this is almost certainly a mistake. When we run this code dot slash puzzle, we see that nothing it prints out and that's because we don't have a cout. So, I can go ahead and open up the source code for this and I'm going to add a little bit of cout to this code. So, here on line 10, I'm going to include iostream and then I'm going to go ahead and cout the surface area, which is the value a and I'm going to cout the volume, which is the variable v. So, here in the source code, all of that is two cout statements on line 24 and 26 in the provided code, which outputs the value of the surface area and outputs the value of the volume. Let me go ahead and save this and close the editor, remake it and then run this code dot slash puzzle. Here we see the surface area and the volume are zero, that's absolutely not what we expected, but was a result of that memory being reused by the system for another purpose, that just happened to fill up that memory with zeros. So even though the cube should have had a volume of several thousand, the cube that actually has a volume zero, because that memory was overwritten. Because we returned a value from a function as the function runs, so the big takeaway from this puzzle is, to understand how memory is actually allocated, how memory works and to never return a reference to a local variable. So, you'll real experience this but once you start working with pointers especially once we get into this week five and six of this course, you're going to see that this stuff is going to come up and you're going to always be mindful of exactly what you're doing with memory. Before we wrap this up, let's look at one final piece of code to cement everything together. So, here in our final program today, we have a program that sort of does a little bit of everything, so that we have a complete understanding of how we expect the system to work. Going with our standard example before, we have a memory allocated called num and it has the value of seven and then we expect the first thing you get printed out is num has seven, the address is going to be a very, very large number. The number is going to be slightly different on each system it runs on, but it will always be large. The second variable we allocate is going to be p. P is a pointer that we set to be the address of num. So, p which has the address of num, the contents of p should be this exact number right here, because the value p contained is the memory address of num. So, the contents of p will be the exact value of the memory address of num. The memory address of p is going to be right below the memory address of num. So, remember the value start high and grow down, so p is memory address should be slightly smaller than num's memory address. Finally, we want to dereference p, the dereference of p says we are going to follow p's pointer and see what it points to p's pointer points to seven, so we expect seven to be output here, we expect this to be a large number slightly smaller so two, three, zero, zero maybe. So, we expect the output to be seven large number, large number, large number slightly smaller than seven again. The last thing we do is, we change the dereference value of p to be equal to 42, so we change num to value to be 42. With nums value of 42, we go ahead and say p is changed to 42 and we print out what num is, so even though we never changed num, we change a dereference value of p to be 42, so num's value here is going to be 42 because that's what's stored in memory. So, the output we expect to see, is we expect to see the number seven which is the original value of num, a high memory address, a high memory address again, a high memory address that's slightly closer to zero, the dereference value of p which is seven, the fact that the dereference value change 42 and then we expect to see 42 print out. Let's run this program and see exactly what happens. So, here in our final program we run dot slash main and dot slash main we see that the output is seven, we see another very large number and then we see this number ends in 148, we see this number 148 again exactly what we expect. Then we say the fourth thing we're going to see, is a memory address that is slightly smaller which is exactly what we see 148 is a larger value,140 is a smaller value, the dereference value of p is seven, which we know that because it points to num itself, when we change the dereference value of p to 42, we see that num also changes to 42. So, we have both seen in a program and understood exactly how this memory interacts with the system and we have code that runs that shows his memory happen in practice. This is one of the hardest things to grasp our head around in C++ especially as it comes to pointers. We're going to followup in the next lecture with heap memory, which includes a lot more pointers and then after that, we're going to just do an entire video on just puzzles with pointers. So, we're going to do a lot of this and I encourage you to play around with this stuff to really get a grasp on exactly what pointers are, because they're a key idea in C++ and they're going to unlock the true power of all the things we're going to do in C++. I'll see you with heap memory in the next lecture.