We know that when a function is called, the parameters are passed to it by value, i.e., the values of arguments in a function call are copied to the parameters of the called function. Since a function parameter is a copy of the argument variable and is local to the function, any change in its value in the body of the function modifies only the local copy and not the corresponding argument in the calling function.
We may often come across situations where we need to write functions that should modify the values of argument variables. For example, consider that we wish to write a function to modify the value of a variable or exchange the values of two variables. The first problem involves modification of a single variable and can be easily solved using a function that returns a value which can be assigned to that variable as shown below.
a= next_val(a);
However, the second problem is more difficult as it involves the modification of two variables. We cannot modify two (or more) variables in the calling function as the function returns only one value (and by default, the function parameters are passed by value, i. e., they are input parameters). Moreover, since the argument variables are in a different function (i. e., outside the scope of the called function), they cannot be directly accessed or modified by the called function. Even declaring these variables to be modified as global variables (so that they can be accessed from any part of the program) is not really useful as the function cannot be used to modify different variables each time it is called (as expected in the case of a function to exchange two values).
The solution to this problem is the call by reference mechanism in which pointer variables are used as function parameters. Thus, the prototype of the swap function, used to exchange the values of two variables, takes the following form:
void swap(int *, int *);
When this function is called, the addresses of the variables to be modified are passed as arguments to the pointer parameters. Thus, to exchange the values of variables a and b, this function is called as follows:
swap (&a, &b);
Note that since the address operator cannot be used with constants and expressions (nonlvalue), we cannot use swap function to exchange values of constants and such expressions.
The swap function uses the call by reference mechanism for both parameters. However, it is possible to mix the call by value and call by reference mechanisms in a single function.
Another situation in which call by reference is useful is when we need to return two or more values from a function, e. g., a function to return both area and perimeter of a circle. The call by reference mechanism is also very useful when passing large structures to a function and may significantly improve program efficiency.
Can you now figure out why we need to pass the addresses of variables to be read from the keyboard in a call to the scanf function? Actually, when scanf is called, it has to store the values entered from the keyboard in argument variables that we pass to it. Thus, the scanf function has to modify the actual arguments through its parameters. As this is possible only by using pointers, the scanf function requires the addresses of the variables.
Example of Function Using Call by Reference
a) Function to exchange the values of variables
Consider the function bad_swap given below that attempts to exchange the values of two variables passed as its arguments.
/* Exchange two integers: INCORRECT FUNCTION */
void bad_swap(int x, int y)
{
int temp = x;
x = y;
y = temp;
}
This function accepts two parameters, x and y of type int. It uses the usual three step procedure to exchange the values of these parameters. This function can be called from some other function, e. g., main,as shown below.
int main ()
{
int a = 10, b = 20;
printf(“Before exchange a = %d b=%d\n”, a, b);
bad_swap(a, b);
printf(“After exchange a = %d b=%d\n”, a, b);
}
When the bad_swap function is called, the values of argument variables a and b are passed to it, i. e., they are copied to function parameters x and y. The bad_swap function correctly exchanges the values of x and y as may be verified by inserting a printf statement after the exchange operation. However, the values of variables a and b in the main function are not modified at all. Thus, the bad_swap function does not exchange the values of the variables passed as arguments which is evident from the output given below.
Before exchange a = 10 b = 20
After exchange a = 10 b = 20
Consider the correct implementation given below that uses the call by reference mechanism.
/* Exchange two integers */
void swap(int *px, int *py)
{
int temp = *px;
*px = *py;
*py = temp;
}
Note that this function has two reference parameters, px and py, which are pointers to type int. As we know, the expressions *px and *py represent int values, the values of the argument variables passed to this function. The body of the swap function uses the three-step procedure to exchange the values pointed to by px and py.
The main function which calls the above swap function to exchange the values of two variables is given below.
int main()
{
int a = 10, b = 20;
printf(“Before exchange a = %d b = %d\n”, a, b);
swap (&a, &b);
printf(“After exchange a= %d b = %d\n”, a, b);
The output of this function is given below.
Before exchange a = 10 b = 20
After exchange a = 20 b = 10
The values of a and b are correctly exchanged now. When the swap function is called, the addresses of variables a and b are passed (i.e., copied) to parameters px and py, respectively. Thus, inside the swap function, the parameters px and py point to variables a and b. Exchanging the values of *px and *py thus actually exchange the values of arguments a and b.
We can call the swap function to exchange different pairs of int variables as illustrated below.
swap (&x, &y); /* variables x and y */
swap(&a[i], &a[j]); /* array elements a[i] and a[j] */
swap(&a[0] [0], &x); /* matrix element a[0] [0] and variable x */
However, the following calls to the swap function are invalid as the address operator (&) cannot be used with a constant or non-Ivalue expressions.
swap (&10, &20);
swap(&(a*b), &(a/b));
swap(&a++, &b–);
swap (&-x, &+y);
Note that the swap function given above exchanges the values of integer variables only. However, if we wish to exchange the values of variables of some other type, we should suitably modify the swap function (just replace the int keyword with the desired type). Thus, to exchange the values of two variables of type float, we can modify the swap function as shown below.
/* Exchange two variables of type float */
void swap(float *px, float *py)
{
float temp = *px;
*px = *py;
*py = temp;
}
Of course, if we wish to exchange variables of several types (say, int, float, char, long double,etc.) in the same program, we can write several swap functions, one for each desired type and give them suitable names such as iswap,fswap,cswap,ldswap,etc.
b) Reverse a vector
In this example, we will use the swap function along with a loop to reverse a vector. A function ivec_rev to reverse an integer vector is given below.
/* reverse an int vector */
void ivec_rev(int a[], int n)
{
int i, j;
for(i = 0, j = n – l; i < j; i++, j–)
swap(&a[i], &a[j]);
}
c) Function to return the sum and average of three numbers.
In this example, we will study how to return two (or more) values from a function. Consider the function sum_avg given below to calculate and return two results, the sum and average of three given numbers.
/*calculate sum and average of three,numbers
Uses call by reference to return both the sum and average */
void sum_avg(double x, double y, double z, double *sum, double *avg)
{
*sum = x + y + z;
*avg = *sum /3.0;
}
This function uses five parameters: value parameters x, y and z, which represent three numbers to be processed and reference parameters sum and avg, which are used to return the sum and average of the given numbers. The function modifies the arguments pointed by the parameters sum and avg using pointer dereference (using *sum and *avg).Note that since the values are returned using reference parameters, the function does not require a return statement.
We can opt to return either of the two values, say sum of three numbers, using the function return value. Such an implementation is given below.
/* calculate sum and average of three numbers. Sum is returned using the function return value and call by reference is used to return average */
double sum_avg(double x, double y, double z, double *avg)
{
double sum = x + y + z;
*avg = sum /3;
return sum;
}
Of course, we can write two separate functions to calculate the sum and average of three numbers. However, it will not be efficient since it will involve the overhead of two function calls instead of one and possibly repetitive calculation of the sum of three numbers.