This episode explains where your variables live when you declare a variable in a method. You will learn that the stack is a good place for these variables and how the memory is managed.
We talk a bit about how microprocessor registers can sometimes be used.
Then pointers and references are compared as well as how you can use references in method parameters to get around the normal behavior that methods have of making a copy of their parameters.
Listen to the full episode or read the full transcript below.
When you declare a variable, most languages require that you give it a type. What actually happens depends on where you declare the variable but let’s say for now that you declare a variable of type int inside of a method.
The variable needs 4 bytes somewhere in memory to hold the integer value. A convenient place for this is on the stack so what happens is the compiler will write code that adjusts the stack pointer when the method is entered and will adjust the stack pointer again when the method returns.
There’s another convenient place to put your integer and that’s in one of the processor registers. Your compiler will decide if it can use a register or if it needs the stack. You have very few registers available and if you declare a lot of variables in your method then some of them will have to be placed in memory.
The stack represents memory that has already been allocated for you to use as long as you don’t go over your limit. Think of the stack in this case like a credit card. Only without the interest. Even a zero interest credit card has a limit though. When the method enters, you charge 4 dollars plus some more for the return address on the card. And when the method returns, you pay it all back.
Your int variable has a name. Let’s call it height because you intend to use this to store the height of your game hero. It also has a value. If you assigned a value at the same time the variable was declared, then the compiler will write code to store that value in the memory that’s ready to hold it. Or you could assign value some time later in which case the memory is still there waiting for when you’ll need it. And while I don’t recommend this, you could just leave everything to chance and leave the variable unassigned. If you never use it, then all that happened is you momentarily reserved 4 bytes on the stack that were never needed. Hey, we’ve all bought things with a credit card that we didn’t really need right? Still gotta pay the bill though. And if you do use the uninitialized variable you might find that sometimes your hero is gigantic and sometimes flatter than a leaf.
Let’s say you also declare another variable that will be an int reference. A reference has to refer to another variable or the appropriate type and that must be done right away. You cannot have an uninitialized reference. But you can make a reference to another variable that has not been initialized.
Another important thing to know is that once a reference is created to refer to a variable, it will always refer to that variable. The reference serves as just another name for the variable. It’s like a pointer because it points to something else but you use it just like you would use a normal variable.
We’ll discuss a similar concept that some language have that make a distinction between reference types and value types in another episode. You’ll see how those reference types are a bit different.
What I’m talking about here are references as used in C++. Understanding the differences between C++ references vs C++ pointers will help you later understand how a language can make it seem like there’re no pointers.
So back to our references.
If you create an int reference called width and assign to it the variable height, then what you’ve done is create a well proportioned hero that will probably roll faster than run because the hero will always be the same width and height. Setting the height to 10 will cause the width to also change to 10. Setting the width to 5 will cause the height to also change to 5. Width acts just like another name for height. In fact, the compiler will not need to reserve any room in memory for the width. There’s no need. Because they will both always have the same value, they might as well share the same memory.
Now where things really get interesting is when you declare a method that takes for example an integer parameter. If you want the method to be able to modify your hero’s height through a method call with this parameter, then you normally need to pass a pointer to your height variable. That means the method must be declared to require an int pointer instead of just an int. Whenever you see a method that takes a pointer to something in its parameters, that’s usually a big clue that the method will try to modify your variable through this pointer. It also means that the method has to be written to use a pointer. And while this isn’t that big of a problem, accessing variables through pointers does make the code look a little more complicated.
What if the method was written to require an int reference though as its parameter? You could now pass your height variable directly. That is after all how we assigned width directly with the height variable. The method will now have a reference to your height variable and can change it. What you just did was bypass the typical behavior that methods always make a copy of anything passed to them. And the method can use the reference just like it would a normal int. So whenever you see a method that is declared to take a reference type, that’s another big clue that the method will want to modify your variable.
There’s a lot more to references than I just described. For example, you just accomplished something with the method that would normally have required a pointer. And sometimes, the compiler might just be able to pull this off without using a pointer by using one of the processor registers instead. Or it might use a pointer. It’s really up to the compiler. From your point of view, it looks like you’re passing the height directly to a method that’s now able to modify the height.