 Hello and welcome back. In this lecture, we are going to look at two paradigms of parameter passing in function calls. Here is a quick recap of some of the relevant topics we have already studied. We have seen the use of simple functions in programs. We have also looked at the contract centric view of programming with functions. We have seen how control flows in a program when a function call happens and when a function call returns. And we have also seen the use of activation records and call stack to implement this flow of control. In this lecture, we are going to look at two predominant paradigms of parameter passing in function calls. These are called call by value and call by reference. And we will also see at the end how we could have functions that do not really return any values. All from an earlier lecture, we had considered an encoding example where we wanted to store the quiz one and quiz two marks of CS 101 students in an encoded form. We have agreed that we would encode the marks in the following way. Given an ordered pair of marks m, n, we would encode it as 2 raised to m times 3 raised to n. And we had also assumed that all marks are integers in 1 through 10. Now, given this problem, we had seen how a C plus plus program for this might look like. Here, I have shown a skeleton of the program. I have only highlighted the parts where function calls are happening. If you recall from the earlier lecture, we had used two different functions, my encode and power. Each of these functions take two integer arguments and returns an integer value. In the main function, we had a for loop which iterated once for every student in the class. And within the loop, we read quiz one marks and quiz two marks of the student and then called the function my encode to compute the encoded form. The function my encode in turn called the function power to compute two raised to quiz one marks and three raised to quiz two marks and then computed the encoded form of the two marks. And of course, the function power just returned the base raised to the exponent. Now, if you look at this function and if you recall how activation records were used in the call stack to keep track of how functions were called and how they were returned, you would recall that when a function, the caller function calls another function, the cally function. So, in this case, let us say the function my encode is calling the function power. So, since we are already inside the function my encode, we are already executing the function my encode. So, the function main has already been invoked. So, in the call stack, we have the activation record for main. In fact, the function my encode has also been invoked. So, in the call stack, we also have the activation record for my encode. And then when the function power gets invoked. If you recall we said that a fresh activation record for this function power would get created and then values of the function parameters. Here there are two function parameters 2 and quiz 1 marks. These values would be copied from the activation record of the caller function to the space allocated for formal parameters of the caller function and this is very important the values are copied from one activation record to another activation record and then of course the PC of the caller is saved so that we know where to return back once function power returns and then some other bookkeeping information is updated and finally the activation record for power is pushed down the stack. Now this aspect where the values of function parameters are copied from the activation record of the caller function to the activation record of the callee function is also called the call by value paradigm. So to recap function parameters will have their values copied from the activation record of the caller to the activation record of the callee. Now you would also recall from an earlier lecture that we said that formal parameters of the callee function are really its local variables and these will not be confused with the variables or parameters used in the caller function when invoking the callee function. So the formal parameters of power are its local variables and are not to be confused with the variables used in my encode when calling power. So what this means is that when my encode calls power the values of the parameters are copied to the local variables of power and then subsequently power does some computation. So the only way in which the callee function in this case power can let the caller function in this case my encode see the effects of its computation is through the value that power returns the value that the callee function returns. Now this is important because this is the only way in which a caller function interacts with the callee function when we use call by value. Now there are also some caveats when we are using call by value we have to remember that any changes done on the local variables of the callee are basically changes done in space allocated on its activation record which is on top of the stack and when the callee returns to the caller this entire activation record is lost. So all these changes are going to be lost. So as I said the local variables of the callee are really on its activation record which are there on the call stack and this is why local variables are also sometimes called stack variables when the callee returns to the caller the entire activation record of the callee is freed up and so all of the changes made to the local variables are lost. Now let us look at how a program for swapping two numbers might look like and here we are going to use call by value paradigm. So here is the main function that reads in two integers and then calls this function swap to which it passes the values of the two integers that it has read in. The function swap takes the values of the two integers and then uses a simple mechanism to swap their values. So it stores the value of one of its arguments in a temporary variable copies the value of the other argument to the first argument and then copies the value of the temporary variable to its first argument to its second argument. So if you go through this you will realize that the values of m and n are swapped and in this case the swap function also returns the value 0 because we declared swap to have a return value of type int. Now if you note here since we are using call by value when this function gets called the values of a and b in main are actually copied to the activation record of swap this m and n are really local variables of swap and then when swap manipulates the values of m and n it is really manipulating its local variables and finally when swap returns all those manipulations all those changes are lost and so main does not get to see the swap that this function try to do. So values of m and n as local variables of swap are swapped but we cannot get to see this when we get back to main because that entire activation record for swap in which its local variables were stored and swapped is now lost when swap returns to main. So how could we fix this? In fact you would notice that the problem arose here because the formal parameters of the function swap were not exactly the same as the variables in the function main from where it was called. In function main we wanted to swap a and b but when we call this function the values of a and b were copied to m and n. So m and n were not really referring to a and b but they just had the values that a and b had. So to solve this problem we could ask that can we let the formal parameters of a function refer to the variables used in main when calling swap. Basically we are asking can caller and cally refer to the same variable. It turns out that in C++ we can do this and this is done by prefixing the arguments of the ampersand operator. So note that we will have a change in the declaration as well. Here also we need to indicate the ampersand operator before the input arguments and what putting this ampersand operator does is that it tells the compiler that these two arguments m and n are not really local variables of swap but their references or aliases to the callers variables that were used to pass parameters. In this case of course the callers variables were a and b. So this is also what is called call by reference paradigm and note that since m and n are not local variables of swap no space will be allocated for them in the activation record of swap and this can indeed lead to significant savings in memory required for the call stack when you have deeply nested function calls because you have less space to allocate in the activation record of a function. Now there are certain caveats when using call by reference as well for example you cannot pass a constant as a parameter of function which is called by reference otherwise the constant could be quote and quote changed by the cally. Here is an example here we have the same swap program but suppose here in the main function I call swap 2, a. Now here we know that m would refer to 2 and n would refer to a but then in this I cannot possibly assign some value to 2. So 2 is a constant I cannot possibly change its value so it does not make sense to swap to an a and you have to remember about this caveat when you are using functions with call by reference. Constants cannot be used as their parameters so we will not allow such invocations of function swap. Now here is a brief comparison of call by value versus call by reference as you would have already noticed call by reference really allows 2 functions the caller and the cally to share variables but this means we also need to be careful because we may end up doing inadvertent updates when a function is called by reference its formal parameters are not local variables but these are variables shared with the caller. However the other variables declared in the body of the cally function are indeed its local variables and we have also seen that by using call by reference we can save significant memory for activation records when we have deeply nested function calls. What about call by value? It is by far the safest you cannot make any change to the caller except through returned value when you use call by value it achieves a clean separation of variables of caller and cally and as a consequence of that it needs to copy the values of the parameters every time a function is called to the activation record of the cally function. So, this can lead to significant memory usage and activation records when we have deeply nested function calls of course the specific choice whether to use call by value or call by reference is very much context dependent. Now having seen parameter passing here is a small note if you look at this function swap that we used over here really there was no scope for errors in this function swap. So, you know when we said return by return 0 I mean since we cannot have an error here this return value is not very meaningful also we know that swap is not being used to compute an integer result. So, we can ask that can we let swap return nothing or no value at all. Now this is different from the functions we have seen so far so far all our functions returned some value and had a type, but now we see that really we can have functions for which return value does not make sense and in fact c++ allows you to do that what you have to do is you have to say the type void as the return type of swap and instead of returning a value you simply return without an argument. So, in summary in this lecture we looked at parameter passing in function calls two paradigms call by value and call by reference we looked at caveats and benefits of each paradigm and we also had a brief glimpse of functions without return values. Thank you.