Call by Value and Call by Reference
In this tutorial, we will learn one of the most important concepts of C programming in detail known as call by value and call by reference. Let’s get started 💪💯
In this tutorial, we will learn one of the most important concepts of C programming in detail known as call by value and call by reference. Let’s get started 💪💯
There are two ways to call a function in C:
To understand the working and how they are different from each other, we need to first get ourselves familiar with the basics of Pointers. Pointers play a very important role in call by reference. We will demystify how in a moment.
Note: pointers are one of the most important topics in C programming, and therefore it will be covered in depth later. Right now, our focus is on the basics to help us better understand the concept of call by reference.
Let’s understand what pointers are anyway.
Definition: pointers are variables that can store the addresses of memory locations.
Pointers are no different from variables. Instead of storing values, they store addresses.
Consider the following definition:
int var = 10;
The C compiler will do the following for the above definition:
The following figure shows that some memory block is assigned to variable var by the compiler with value 10 stored in it. Notice that the memory location has some address to.
We already know how to display the value of a variable. But, what if we want to display the address for some reason? We can use & (ampersand) which is called the “address of” operator as follows:
int var = 10;
printf(“Address of var: %llu”, &var);
Output:
32768
We can display an address as an unsigned integer by using %llu
(long long unsigned) if it’s a 64 bit address. On a 32 bit machine, we can use %lu
to display a 32 bit address. Some compilers may give warning that we are trying to display the memory address as an integer. To avoid this, we can use %p
as the format specifier to display the address in a hexadecimal format. This format specifier is specially meant to print an actual memory address.
Write a program to display the address in hexadecimal format of the following integer variable:
int a = 5;
Answer:
#include <stdio.h>
int main() {
int a = 5;
printf("%p", &a);
return 0;
}
Output:
0x7fd6b44fb8
Explanation:
When I executed the program, I got 0x7fd6b44fb8 as the hexadecimal address of the variable a. You may get a different address, so need not worry if my output is not matching with yours.
X-X-X
Now, what if I create a pointer and store the address of the variable var in it? Then, instead of directly using the variable var, we can refer to it through a pointer.
You might be thinking at this moment, what’s the point of doing all this? Please bear with me. I promise I will not leave you in the middle of the road. The importance of doing all this hard work will be completely clear by the end of this article and believe me it’s worth it 💯
Let’s now create the pointer to the integer variable var as follows:
int *p;
p = &var;
In line number 1, the pointer p is declared and in line number 2, the address of the variable var is assigned to it. Notice that pointer p is just like a normal variable, it has the data type, the name of the variable, but to differentiate it with a normal variable, an asterisk is added before the name. But why an asterisk? This symbol has a special meaning. We know, if we use asterisk with two operands, it will act as the multiplication operator just like the following:
int a, b=10, c=20;
c = a * b;
But if we use an asterisk with a single operand, it acts differently. It no longer acts as the multiplication operator, it becomes what is called the value at address operator. So, the declaration int *p; means “the value at address stored in p is an integer.” This means pointer p is holding the address of some integer value.
Note: when we declare a pointer, the data type specified is not the data type of the pointer itself, it represents the type of data whose address is stored in it (the major difference between a normal variable and a pointer worth noting). In our example, p is the pointer to integer value stored in the variable var as p is holding the address of var.
Apart from declaring a pointer, an asterisk is used to access the value stored in a specific memory location. That’s why it is called the “value at address” operator. The following code snippet demonstrates the use of asterisk (*):
int a = 5;
printf(“%d”, *(&a));
Output:
5
First, by using the ampersand (address of) operator, we can get the address of the variable a. Then, by using the asterisk (value at address) operator we can get the value stored at the address of a which is 5 according to the example. Therefore, 5 is displayed on the screen.
Note that there is no need to do this much work to access the value stored in variable a. Instead, we can directly use the name of the variable as follows:
int a = 5;
printf(“%d”, a);
Output:
5
At this point, you know two different ways to access the value of a variable. Great 😃
Congratulations 👏 we have come to the end of this section. Don’t worry if some concepts are unclear. As we progress, we will revisit the basics many times throughout the course. For now, just keep the following points in mind:
Consider the address of a is 0061FF1C and the address of p is 0061FF18. Determine the output of the following program:
#include <stdio.h>
int main()
{
int a = 10;
int *p = &a;
printf("%p\n", &a);
printf("%d\n", *(&a));
printf("%p\n", p);
printf("%d\n", *p);
printf("%p\n", &(*p));
printf("%p\n", &p);
printf("%d\n", *(*(&p)));
return 0;
}
Please try solving the above problem on your own first, and then look at the solution for comparison purposes.
Solution:
The output is as follows:
0061FF1C
10
0061FF1C
10
0061FF1C
0061FF18
10
The explanation is as follows:
#include <stdio.h>
int main()
{
int a = 10; // variable a of type int holding value 10.
int *p = &a; //p is the pointer to the integer variable a means it is storing the address of a.
printf("%p\n", &a);
printf("%d\n", *(&a));
printf("%p\n", p);
printf("%d\n", *p);
printf("%p\n", &(*p));
printf("%p\n", &p);
printf("%d\n", *(*(&p)));
return 0;
}
Before moving forward, let’s discuss the two main terms related to function calls:
Actual arguments: these are the values passed to a function by its caller.
Formal parameters: these are the variables at the receiving end. They receive values passed by the caller in the function definition.
The following example shows the formal and actual parameters in action:
#include <stdio.h>
int add(int a, int b) // formal parameters (variables) where a = 10 and b = 20.
{
return a + b;
}
int main() {
int result = add(10, 20); // actual parameters (values 10 and 20) passed by the caller.
printf("%d", result);
return 0;
}
Output:
30
Is the following program valid?
#include <stdio.h>
int fun(int a, int b, char c)
{
return a + b + c;
}
int main() {
int x = 3 + 5 - 10 * 10 / 2;
printf("%d", fun(x, 67 / 3, 'a'));
return 0;
}
Answer:
Yes. The above program is absolutely valid. Formal and actual arguments can be of any type.
The result is evaluated as follows:
We have already learned how to call a function in the introduction to functions tutorial. There we took an example of adding two numbers. Let’s use the same example to understand the concept of call by value:
#include <stdio.h>
int add(int a, int b) {
int sum;
sum = a + b;
return sum;
}
int main() {
int result;
result = add(10, 20);
printf("Result: %d", result);
return 0;
}
In the above example, we are calling the add()
function within main()
and we are passing values 10 and 20 to the variables a and b respectively. This is called call by value. As the name suggests, call by value means calling a function and passing values to it as actual arguments.
This one is easy to understand, but what if we pass variables as actual arguments? Passing variables as arguments does not make any difference. See the modified version of the previous example:
#include <stdio.h>
int add(int a, int b) {
int sum;
sum = a + b;
return sum;
}
int main() {
int result, x = 10, y = 20;
result = add(x, y);
printf("Result: %d", result);
return 0;
}
This time the actual arguments are variables, and passing variables as arguments is the same as passing their values. So, this is also called by value.
Now, as we have understood call by value, we are fully ready to understand the concept of call by reference.
Calling a function by reference means passing addresses (in place of values) of variables as actual arguments. By using & (address of) operator, we can access the address of a variable. Consider the following example for better understanding:
#include <stdio.h>
int add(int *a, int *b) {
int sum;
sum = *a + *b;
return sum;
}
int main() {
int result, x = 10, y = 20;
result = add(&x, &y);
printf("Result: %d", result);
return 0;
}
Output:
Result: 30
Explanation:
It can be observed that within the main()
function, we are calling the add()
function, but this time we are passing the addresses of x and y, and not values, as arguments. But in the receiving end, we cannot have simple variables to store addresses, we need pointers. In the basics of pointers section, we learned how pointers are used to hold addresses. So, that’s why in the receiving end, pointers a and b are declared and they will receive addresses from the caller in the same order in which they are passed as arguments.
Apart from this, within the add()
function you can observe asterisk (*) is placed in front of a and b in the expression sum = *a + *b
. It is important to do this because a and b are not normal variables, they are pointers holding addresses of variables x and y. And as we learned in the basics of pointers section, asterisk (*) is called the “value at address” operator. As a is the pointer to variable x in the main()
function, using an asterisk in front of it will give the value stored in variable x. Similarly, b is the pointer to variable y, and hence using an asterisk in front of it will give the value stored in variable y. So, sum = *a + *b
is the same as sum = 10 + 20 = 30
. This value will be returned to the caller and that’s why we got 30 as the output.
But why are we doing all this hard work? Passing addresses and then accessing the values stored in those addresses does not make any sense. Aren’t we happy with call by value? After all, call by value is pretty straightforward.
We definitely have done a lot of work in the last example to access values of variables x and y. Passing by value makes more sense. But believe me, all the effort made so far is not wasted. The importance of call by reference will be evident through a popular use case in programming – swapping two numbers. So, let’s dive in.
Let’s say we have two numbers 10 and 20 within variables x and y respectively inside the main()
function. We are required to define a function which is capable of swapping values within variables x and y. This means after the function completes its execution, variable x will hold value 20 and y will hold value 10. How to achieve this?
To swap numbers, we can use a temporary variable as follows:
// Considering x and y are already defined.
int temp;
temp = x;
x = y;
y = temp;
After executing temp = x
, variable temp will hold value 10. Because of x = y
, the value of y is assigned to x and this means the new value of x is 20. Now, we can assign value 10 to variable y using the statement y = temp
. In this way, the two numbers are swapped.
Before execution of the code snippet:
x = 10, y = 20
After execution of the code snippet:
x = 20, y = 10
To verify, run the following program on your computer:
#include <stdio.h>
int main() {
int x = 10, y = 20, temp;
temp = x;
x = y;
y = temp;
printf("x = %d, y = %d", x, y);
return 0;
}
Output:
x = 20, y = 10
See! It works 😌
But, we have been asked to define a function to handle swapping. So, let’s create the function swap()
and include the code snippet we just saw to swap two numbers within the swap()
function as follows:
void swap(int x, int y) {
int temp;
temp = x;
x = y;
y = temp;
}
Notice that the return type of the swap()
function is void because this time we are not returning the result. We are just swapping numbers.
Now, within the main()
function we need to call the above function and pass appropriate values to the parameters. The complete program is as follows:
#include <stdio.h>
void swap(int x, int y) {
int temp;
temp = x;
x = y;
y = temp;
}
int main() {
int x = 10, y = 20;
swap(x, y);
printf("x = %d, y = %d", x, y);
return 0;
}
Output:
x = 10, y = 20
Explanation:
It seems like the numbers are not swapped. Variables x and y are still holding their initial values. What’s going on here? We know the logic we have written to swap values is correct (as we verified this already). It works within the main()
function, but not within the swap()
function. Why? The answer lies within the fact that we are calling the swap() function by value. This is the root cause of the problem. Let’s understand properly why call by value is the problem.
Call by value means calling a function and passing values of the variables as actual arguments. While calling the swap()
function, we are passing values of x and y which will be received by the parameters x and y of the swap function. These parameters are defined within the function, they are not the same as the variables defined within the main()
function. So, in the swap()
function, x and y are newly defined variables which have received values 10 and 20 respectively. Now, within the swap()
function, due to the swap code, the numbers 10 and 20 are swapped within these variables only. Variables x and y defined in the main()
function are unaffected by this change. The following illustration demonstrates the same:
As soon as the swap()
function finishes execution, variables x and y defined as parameters will be destroyed, and variables defined in the main()
remain unaffected by the work done in the swap()
function. Conclusively we can say the swap()
function we have defined is useless 😔
You might be thinking why are we not returning x and y values from the function? This is because a function can return only one value. Here, the requirement is to return values stored in x and y both. So, we are not left with this option.
Then, what’s the way out? A simple way out is calling a function by reference 🥳. Consider the following program:
#include <stdio.h>
void swap(int *p, int *q) {
int temp;
temp = *p;
*p = *q;
*q = temp;
}
int main() {
int x = 10, y = 20;
swap(&x, &y);
printf("x = %d, y = %d", x, y);
return 0;
}
Output:
x = 20, y = 10
Explanation:
This time we got our desired output, but how? Understanding this concept is not difficult if you focus for a moment. Within the main()
function, the swap()
function is called, but this time we have used the address of (&) operator in front of variables x and y to pass the addresses of x and y. This is call by reference. We are not passing values this time, we are passing addresses (in other words, references). In the receiving end (i.e. in the swap()
function), parameters are not variables, they are pointers. As mentioned earlier, to receive addresses, we need pointers. Simple! So, pointers p will hold the address of x and pointer q will hold the address of y.
Now comes the most interesting part. This is where all the magic has happened. Let’s understand each line properly.
The entire process is summarized in the following illustration:
So, in this way, we are making changes to the original variables by using call by reference. From all this discussion we can understand that call by reference is useful in situations when multiple variables of some function need to be affected, as the result of some operations performed on them, in some other function. This is the use case of call by reference 🙂
Use call by value when you don’t want the original variables to change. It is useful when you just want the copy of the variables.
Use call be reference when you want the changes to reflect in the original variables, just like in case of swapping two numbers.
How to get the address of the following variable?
int var = 10
ampersand (&) must be used before the variable name to get its address.
Which of the following is the correct way to declare a pointer in C?
int *p; is the only valid way out of the ways given in the options. Although, we are allowed to add spaces in between the way we want. For example, int *p, int* p; int * p; are all valid ways to declare the pointer p.
What is the main disadvantage of call by value which is also the advantage of call by reference?
call by value uses more memory because every time when a function is called by value, the copies of the actual values are passed as arguments and at the receiving end, new variables (parameters) should be defined to receive these values. On the other hand, in call by reference, the original variables are referred to in the called function in place of creating new variables.
Your score is out of
In C programming, actual arguments can be passed to a function in two ways: by value or by reference. By default, functions are called by value. If we want to call a function by reference, then we need to pass addresses as actual arguments. In this tutorial, we learned the significance of the address-of (&) operator, the differences between call by value and call by reference, and in which situations call by value and the situations where each method is useful. I hope I made the concepts clear. If you have any doubts, then post your doubts in the comment section of this tutorial.
Leave a comment