6.3.1. Pass-By-Value

Time: 00:02:52 | Download: Large, Large (CC), Small | Streaming, Streaming (CC) | Slides (PDF)

Pass-by-Value (also known as pass-by-copy) is the default argument passing technique for all data types in a C++ program except arrays. It is the only argument-passing technique provided by C and Java and is, therefore, the technique most familiar to many programmers. Pass-by-value has two advantages over other passing techniques:

  1. Arguments in the call can take many forms: constants, variables, or more complex expressions
  2. The function receives a copy of the data, which means that it can modify the data without affecting the original data in the call - the original data is protected

Pass-by-value also has one disadvantage not shared by the other two techniques:

  1. If the argument data is large (e.g., a large object - an instance of either a structure or class), copying the data can take a significant amount of time

Figures 1 and 2 demonstrate pass-by-value. Both figures present a simple program on the left and an abstract representation on the right that illustrates what occurs in memory when the program runs. Program comments articulate the key program operations with the corresponding changes happening in memory. The addresses of the variables a (0x12) and p (0x34) are arbitrary.

Passing Simple Data By Value

Figure 1 is based on an integer but is consistent with any of the simple, fundamental data types like char, int, or double.

void func(int p);

int main()
{	
	int  a = 5;	// step 1

	func(a);	// step 2
}

void func(int p)
{
	p = p + 1;	// step 3
}			// step 4
An illustration that represents variables a and p as squares. Each variable has a distinct value written inside the square; in step 1, there is a 5 in the variable's square. Each variable has an address written outside the square; in this example, the addresses do not change or play a role in the data-passing operation. Step 2, the content of a, 5, is passed to p. We illustrate the pass by writing 5 inside the variable p's box. Step 3 increments the 5 in b 6; variable a remains unchanged. In the last step, the function ends, and the box representing p disappears; box a still contains a 5.
Pass by value with simple data.
  1. The variable a is defined and initialized with the value 5
  2. The expression in the function call is evaluated - in this example, the expression is simple, and the evaluation is trivial. A copy of the value, 5, is passed from the function call to the local (parameter) variable p (in essence: p = a;)
  3. The increment operation takes place in the function; the local variable p is incremented to 6
  4. The function terminates and the local variable p is deallocated. Changes to the local variable p do not affect the original argument (variable a) and a remains unchanged

Passing Structured Data By Value

Figure 2 is based on a structure but is also valid for objects instantiated from a class.

struct part
{	char	type;
	int	id;
};

void func(part p);

int main()
{	
	part  a = { 'd', 10 };	// step 1

	func(a);		// step 2
}

void func(part  p)
{
	p.id = 1000;		// step 3
}				// step 4
Another illustration representing variables a and p as squares, but this time, the variables are structure objects, and each square holds two values, one for each field. In step 1, there is a 'd' and a 10 in variable a's square. Step 2 calls a function, and both values stored in a are passed to or copied to the two fields in p. Step 3 changes the id field of variable p to 1000. The last step ends the function and removes the box representing b; box a still contains 'd' and 5.
(a)(b)
Pass-by-value with structured data.
  1. The structure variable a is defined and both fields are initialized
  2. Function func is called, and the contents of the argument structure (variable a) are passed or copied from the call to the local parameter (variable p - in essence: p = a;)
  3. The id field in structure p is modified by assigning 1000 to it, but variable a is unchanged
  4. The function terminates, and the local variable p is deallocated; variable a remains unchanged; that is, changes to the local structure variable do not affect the original structure

Pass-By-Value: Advantages and Disadvantages

Pass-by-value offers programmers two advantages over the other passing techniques. First, function arguments may be any expression - not just variables. The program evaluates the expressions before the call and passes the resulting value to the function parameter. Second, a function can modify a parameter without inadvertently changing the data at the calling location.

(a)
print_table(principal, P * R / (1 - pow(1 + R, -N)), apr / 12, years * 12);
(b)
void print_table(double balance, double payment, double R, int N);
{
	...
	balance -= (payment - to_interest);
	...
}
Pass-by-value advantages and disadvantages. To demonstrate for-loops, the Chapter 3 mortgage.cpp example printed an amortization table. Imagine we rewrite the program, collecting the code that prints the table into a function called print_table. A possible function call and an excerpt from the function illustrate the pass-by-value advantages.
  1. Expressions form the second, third, and fourth arguments. The program evaluates the expressions before transferring control to the function. The first expression (the second argument) is a little awkward, but the last two are quite reasonable. The example doesn't fit the original program because the expression values are needed elsewhere, suggesting that saving the values in variables is more efficient.
  2. The principal is the amount originally borrowed and remains unchanged once entered into the program. The balance is the outstanding, unrepaid amount the borrower owes. Initially, the balance is the same as the principal, but it decreases each time the borrower makes a payment. print_table can change balance without affecting principal.
Pass-by-value has two disadvantages:
  1. Changes a function makes to parameters passed-by-value are not propagated back to the caller through the parameters, even when desirable.
  2. The compiler implements pass-by-value by passing a copy of the data to the function. Consequently, as the data size increases, for example, a structure with many fields, it takes increasingly longer to copy the data to the function.