So how do function calls work.
Functions use an area in memory called the 'function
call stack' or program stack.
A stack is analogous to a stack of books or a stack of dishes.
If you place a book on top of the stack, then you must remove that
book before removing any others.
This is referred to as last in first out or LIFO.
Stacks also use the terms push when you put an item on top of the
stack and pop when you remove an item from the top of the stack.
In the case of a c++ program, these items are called stack
frames or activation records.
All it is, is a collection of information that represents
an active function.
So this is where parameters are stored, local variables,
the return address and more.
Each time a function is called, an activation record is created, and
it's pushed onto the call stack.
When the function terminates, we pop its activation record off the
call stack, and now the top of the stack is the function that just
called the one we just popped off.
The call stack works in a very orderly manner.
You can't jump into or out of the middle of the stack.
You must follow the lifo rules.
Also remember that the call stack is finite in size.
If you activate too many functions on the call stack, then it's
possible to run out of stack space.
This results in a stack overflow error, which is usually
an unrecoverable error and your program will terminate.
The best way to understand how function calls work
is to show you visually.
Let's do that now.
In order to understand how function calls work, we really need to
understand how memory is laid out.
Here, let's say that this is the memory for our program.
It's divided into segments.
So here, we've got the code area.
This is where the code resides.
Then we've got an area for static variables or for global variables.
That's where they're stored right here.
And here's the area for the stack.
This is the function call stack.
And this is what we're really concerned about in this video.
So this is the area I'm going to concentrate on in this video.
Then we've got another area in memory here called the
heap or the free store.
We'll talk a lot about this area when we talk about pointers and
dynamic memory allocation. So in this video, keep in mind that what
I'm talking about is this area right here, that's the function call stack.
So when we call functions and they finish and they pop off the stack,
this is what we're talking about, this area in memory right here.
Okay, so I'm in the IDE.
And I'm in the section 11 workspace in the how function calls work project.
And I've got a real simple example here that I'm going to walk
through in detail in a second.
But before I do that, let's concentrate and look at
what typically happens when one function calls another.
So I've written it out here, and this is not the only way to achieve this.
There's a lot of different ways to get to the same end.
But this is what typically happens.
So suppose I have a main and that main calls a function called func1.
That's what's happening here.
I've got main and it's calling func1.
Okay, and what happens?
Well, main pushes space on the stack.
Remember, everything here is based on the stack.
For the return value that function func1 is going to
return something to main.
Main is the one that needs to get that result once that function is done.
We need to push space for the parameters that are
being passed in if any.
In this case, we've got two right here x and y.
And we need to push the return address.
That's important because function1 needs to know where to come back to.
Then we transfer control over to function1, which is
basically an assembly language instruction called jump.
You just jump over there, and you're you're off and running,
so control is transferred.
Now on function1 side, it pushes the address of the
previous activation record.
That's basically moving a stack pointer.
That way you know where the top of the stack is.
You push any register values that need to be restored.
Then you perform the code in function1.
When the code is finished, you restore the register values that
way main is where it was before.
You restore the previous activation record, right, you basically pop
all this stuff off the stack.
You store any function result where main wants it, you can
see main gave you the address.
And you transfer control back to main.
So you jump back to that return address that main pushed.
On main side now, remember, function1 is now done.
So main knows where the parameters are, and it knows
where the return values are.
So it needs to pop that information off the stack to clear up the stack
and also grab those values if it can.
Okay, so this is the general flow.
We're not going to cover all the details here.
I'm more interested in the parameter side of it.
So what I'm going to do is I'm going to walk through this example, and I'll
draw a function call stack over here.
And we'll go through it in detail, so you can see exactly what's going
on from the parameter perspective.
I'm not going to worry about stack pointers and static links and
dynamic links and all the stuff that's on an activation record.
But more generally, let's worry about the parameter passing so you
can really understand what's going on because it's important that you
understand this, especially when we learn about recursion later on.
Okay, so let's walk through this example.
Program execution starts at domain.
So main is going to be activated.
It's got a function activation record because it is a function.
So let's assume here main is on the stack already.
And main has an x, it's got a y and it's got a z.
So we need space for those local variables.
You can see x is initialized to 10, y is initialized to
20 and z is 0, in this case.
Okay, so now main is right here, we're executing this piece of
code, z equals function1 with x y.
So we're calling function1.
So what happens is main stops what it's doing.
It allocates space for x y.
And I'm just going do this conceptually.
So we don't waste a lot of time dealing with the stack
and pointers and things.
But let's suppose that at this point, we get -- we're
going to push an activation record on here for function1.
And this is a little different from the example I showed you
that i just walked through but it's a little easier to draw.
So this is where we're at.
So now we're at function1.
Function1 is now on the stack, and we need space for a, b, and result.
Okay, so let's say we've got a, we've got b and we've got result.
We're doing pass by value here.
So what's going to happen is the x hooks up with the a and the
y hooks up with the b, right?
There's your actual formal correspondence here.
So x is 10 and that goes copied into the a.
Y is 20, in this case.
So we're going to copy that to the b.
Okay, so this is where we're at right now.
Now let me clear a little bit of this up.
All right, so that's where we're at now.
Now we start executing this function, function1.
So it's going to add a and b.
Okay, so that would be 20 plus 10.
So this gives me a 30.
And it's going to store that value into result right here.
So now result gets a 30.
Originally result was 0, right, because I zeroed it out here.
I didn't write it on here but you get the idea here.
Now we're going to call function2, and we're going to pass in result a and b.
Okay, so again, let's stop what we're doing here.
We need to come back and finish this code.
So we're here, and now we're activating function2.
And all along those stack pointers are moving along here.
This is the part that I'm not going to worry about drawing here.
Okay, so now we're in function2.
We need space for x y and z.
But notice x is a reference parameter.
Okay, so we'll have to deal with that.
So we've got x y and z.
All right, so let's do x y and z.
All right, let's do y and z first.
We're going to pass those by value.
What is y and z.
Well, we've got 3 here.
We've got result, which is corresponding to x, a corresponds
to y and b corresponds to z, right.
So y gets the value of a.
The value of a was 10, so y is going to get 10.
And z gets the value of b.
So b was 20, z is going to be 20.
Okay, now let's talk about that reference parameter.
Remember, when we talked about reference parameters, we said
that there were aliases, right.
So in this case, x is an alias for result right here.
So x is an alias for result.
So any changes I make to x, result will be updated, right, because
that's the whole idea, that's the whole point of pass by reference.
Now we start executing this code.
The code says x equals x plus y plus z.
That's what that means right there.
Okay, so we've got y and z here, which are 10 and 20.
So this becomes 10 plus 20.
And then what's x.
Well, x is a reference to result.
Result is 30.
So we're going to grab the 30 and add those two together.
That gives us 60.
So now we're going to say x equals 60.
Well, again, x is an alias for result.
So what we're doing is we're updating that guy to 60 right here.
Okay, now function2 is done.
So what happens, it gets popped off the stack.
So let's do that.
So function2 is now gone from here.
All that's gone.
I'm back in function1.
Well, where did function1 leave off.
It left off right here.
We're back from the function call now.
So it needs to return result.
What's result 60.
Where is it going to return result two? Right here.
Remember, result right there is the result of that function call.
So what I'm going to do here is I'm basically going to say z equals 60.
So that's what's happening here.
I'm popping off the stack now.
So function1 is done.
All this is gone.
I'm back to main, and all main has to do is that one output
statement right here, which displays z, and z is now 60.
And the program's done.
Okay, so this gives you an idea of conceptually what's
going on with function calling.
Now I skipped a lot of these steps here on purpose, because
there's a lot to draw and it gets really, really cluttered.
But think about all this as function call overhead.
You really don't have to worry about pushing registers
and transferring control.
All that's done for you by the compiler.
But there is a certain amount of function call overhead here.
So let's run this program and see that we indeed get a 60.
And there it is, there's the result.
Your 60.
Okay, so hopefully, this works.
We'll do this again when we talk about recursion.
That's when a function calls a function calls a function calls itself.
So it's really important to understand what's going on
with the stack call frame.
So we really know what's what's being passed and what's being returned.
And you can see here that if we don't have a way to unwind to get
back to where we were, we could just really just run out of memory right
and get a stack overflow error.