Passing arguments to a function (2022)

In this section we discuss how the value of the actual arguments in a function call are passed to the function.Consider again the function sum.

1234567
// Return the sum of integers from 1 to nint sum(int n) { int result = (n * (n + 1)) / 2; return result;}

First, it is important to keep in mind that functions only have access to the local variables defined in the function’s body (e.g. result)and the functions formal arguments (e.g. n).

Consider the following main which calls sum(a) (line $8$).

12345678910
int main() { int a = 0; std::cout << "Enter a:\n"; std::cin >> a; if (a > 0) { std::cout << sum(a); //call function sum for argument a }}

Function sum does not have access to variable a because this variable is defined in the main.How does then function sum have access to the value of variable a?The answer is that call-by-value is used when function sum is called.

Call-by-value

Call-by-value is a technique used when a function is called that copies the value of the actual arguments into the variablesrepresenting the formal arguments of the function. In other words, the formal arguments of the function receive a copy of the corresponding actual arguments in the function call.The correspondence between formal and actual arguents is done by position in the argument list, i.e. the first actual argument is copied into the first formal argument,the second actual argument is copied into the second formal argument, and so on.

Consider the example above and the function call in line $8$ of the main.Then, the formal argument n of function sum receives a copy of the intstored in the actual parameter a.Note that the function does not have access to variable a.Function sum has only access to a copy of a which is stored in the variable n.

Passing arguments to a function (1)

Thus, using call-by-value implies that the function can modify the value stored in the formal argument, n,without modifying the actual argument, a.For instance, function sum could be written as follows, without running the risk of modifying the actual argument a.

1234567
// Return the sum of integers from 1 to nint sum(int n) { n *= (n + 1); return n/2;}

A disadvantage of call-by-value method is that it requires copying which consumes both memory space and time.Thus, call-by-value can be quite inefficient when applied to arguments that occupy many bytes.Another limitation of call-by-value is illustrated by the following example.

Assume one wants to write a function that swaps the values of two variables.This function is actually a very central function in many libraries,like the C++-standard library, because many important algorithms rely on swapping values of two variables (such as sorting functions).Let’s then make our first attempt to write such function. The function below would not accomplish this task.

void swap(int x, int y) { int temp = x; x = y; y = temp;}

Consider the following code excerpt which calls function swap given above.

int main() { int a = 6; int b = 8; swap(a,b); // variables a and b are not modified: a stores 6 and b stores 8 std::cout << "a = " << a << " b = " << b << "\n";}

Since call-by-value is used, the value of the actual arguments a and bare copied into the formal arguments of the function, x and y, repectively.The function swap does not have access to the variables a and b.Instead, the function swaps the values in the variables x and y(i.e. the copies of a and b are swapped).The function swap provided above is just useless, though it compiles (and executes).

So, how can one write in C++ a function that swaps the values of two variables?The answer is to use call-by-reference, instead of call-by-value, to pass the arguments a and b to the function.

Call-by-reference

Call-by-reference is a technique used when a function is called where the formal arguments of the function refer to the actual arguments.In other words, the formal parameters of the function give access the the variables representing the actual parameters used in the function call.Consequently, the function called can modify the actual parameters which are variables outside the function.

Let’s re-write the function swap but now we use call-by-reference, instead of call-by-value.

123456
void swap(int& x, int& y) { int temp = x; x = y; y = temp;}

Observe the code carefully. Nothing changed in the function’s body, i.e. it’s still the same logic. The only modification is in the list of formal arguments of the function:

void swap(int& x, int& y);

The ampersand (&) is used before the formal arguments x and y.Consider again the function call swap(a, b).

int main() { int a = 6; int b = 8; swap(a,b); // variables a and b are modified: a stores 8 and b stores 6 std::cout << "a = " << a << " b = " << b << "\n";}

The formal arguments, x and y, refer to the actual arguments a and b, respectively.The execution of the statement x = y; (line $4$) takes into account that x and yare kind of special variables that refer to other variables outside the function swap andthe value of the variable referred by y (i.e. y refers to variable b) is stored in the variable referred by x (i.e. x refers to variable a).Thus, the statement x = y; (line $4$ of function swap) effectively modifies the value of the actual parameter a.A similar idea applies to statement y = temp; (line $5$) stating that the value stored in variable temp is copied into the variable referred by y (thus, the value stored in b is modified).The figure below illustrates this idea.

Passing arguments to a function (2)

At first sight, the concept of a variable referring to another variable may seem quite vague (almost illusive).You may wonder but how does a variable refer to another variable? How is this implemented by the computer?Well, that’s business for the compilers and compilers may implement this concept in different ways.For instance, the formal arguments of function swap may receive the memory address of the actual arguments,i.e. x stores the memory address of variable a and y stores the memory address of variable b.In this way, function swap can access the actual arguments, a and b, which are variablesdefined outside the function.Indeed, a powerful method to pass information to functions!! But this power has also its dangers, if misused.

Consider again function sum but now using call-by-reference.

1234567
// Return the sum of integers from 1 to nint sum(int& n) // Ohoops, call-by-reference is a bad idea here!!{ n *= (n + 1); return n/2;}

Consider also the following main which includes a call to function sum.This piece of code will display “sum(272) = 136” which is not the correct output we would expect(the correct output is “sum(16) = 136”).

int main() { int a = 16; int result = sum(a); // Ohoops, variable a has unexpectedly been modified: a stores 272 std::cout << "sum(" << a << ")=" << result;}

The bug lies in the fact that the formal argument n gives access to variable a.Thus, the statement n *= (n + 1); in the function (line $4$) modifies the value of the variable referred by n,i.e. variable a is modified.

Good programming practices

The table below compares and contrasts both methods used in C++ to pass arguments to functions: call-by-valueand call-by-reference. Similar methods also appear in other languages such as C#, Java, PHP, Javascript, and Python.

Call-by-value Call-by-reference
Meaning A copy of the actual arguments is passed to the function. The formal arguments refer to the actual arguments. Usually, the memory address of the actual arguments is passed to the function.
Advantages Actual arguments cannot be changed accidentally by the function. Efficiency. It does not require to make copies of variables.
Disadvantages Copying variables with many bytes consumes extra time and space. Accidental changes in a formal argument also affect the value of a variable outside the function.

The C++ core guidelines describes good programming practicesabout the use of both methods and the C++ community expects the programmers to follow these good practices.The table below gives a simple (yet incomplete) summary of some of those good practices.

Call-by-valueCall-by-reference
void f(T x) { //Do something with x}
void update(T& x) { //Do something that modifies x}
Use when type T is cheap to copy, i.e.T occupies few bytes (like int, double, bool), andone wants to ensure that the function does not modify the actual parameters.Use if the function should update the value storedin the variable referred by x.

In C++, if an argument x is passed by reference to a function f, then programmers assume that calling f will modify x.

One may wonder how to handle the case when a variable x occupying many bytes is passed to a function, though the function is not meant to modify x. This point is discussed in the next section.

Top Articles

You might also like

Latest Posts

Article information

Author: Fr. Dewey Fisher

Last Updated: 10/16/2022

Views: 6461

Rating: 4.1 / 5 (62 voted)

Reviews: 85% of readers found this page helpful

Author information

Name: Fr. Dewey Fisher

Birthday: 1993-03-26

Address: 917 Hyun Views, Rogahnmouth, KY 91013-8827

Phone: +5938540192553

Job: Administration Developer

Hobby: Embroidery, Horseback riding, Juggling, Urban exploration, Skiing, Cycling, Handball

Introduction: My name is Fr. Dewey Fisher, I am a powerful, open, faithful, combative, spotless, faithful, fair person who loves writing and wants to share my knowledge and understanding with you.