Pass by Value vs. Pass by Reference

From SwinBrain

In this article we are going to look at the two parameter passing mechanisms available in many programming languages. These are called Pass by Value, and Pass by Reference. These two parameter passing mechanisms are very important in procedural programming languages such as C, and Pascal.

Introduction: The material in this page aims to introduce important concepts rather than provide a detailed examination of the topic.

Contents

Passing by Value

The default parameter passing mechanism in many programming languages, including both C and Pascal, passes argument values by value. Lets look at an example to illustrate this. Consider the following code provided in both Pascal and C.

program TestPassByValue;
 
procedure DoubleValue(val1: Integer);
begin
  val1 *= 2;
end;
 
procedure Main();
var x: Integer;
begin
  x := 10;
  DoubleValue(x);
    
  WriteLn('The value of x is now... ', x);
end;
 
begin
  Main();
end.
#include <stdio.h>
 
void doubleIt(int val1)
{
  val1 *= 2;
}
 
int main()
{
  int x = 10;
  doubleIt(x);
  
  printf("The value of x is now %d\n", x);
}

When this code is executed you can see that the value of x does not change. Why?

To answer this question we need to look at how the value is passed between the procedures. With Pass by Value the value of the argument is copied to the parameter. The called routine then works with the copied value. When the routine ends the copied value is lost...

Passing by Reference

So what is Pass by Reference? With pass by reference the argument itself is passed to the parameter. The following code illustrates how to achieve this result using Pascal. The change is actually quite small... The val1 parameter now has the var modifier before it. This indicates to the compiler that you want to use Pass by Reference. When this program is executed the value of x is indeed doubled.

program TestPassByReference;
 
procedure DoubleValue(var val1: Integer);
begin
  val1 *= 2;
end;
 
procedure Main();
var x: Integer;
begin
  x := 10;
  DoubleValue(x);
    
  WriteLn('The value of x is now... ', x);
end;
 
begin
  Main();
end.

Illustrating parameter passing

To help understand how these two parameter passing mechanisms work we can mimic the execution of the program on paper using a couple of tables. Lets start by walking through the tables that we will use to do this. The first table we will use is the Memory Table that lists the "memory location" of values used by the program. For simplicity we will assume that each slot is able to store any value. The other table is the Routine Table, inside which we list the variables and their locations. We will use one Routine Table per routine currently executing in the program.

Passing by Value

Lets run through the TestPassByValue program.

  1. program TestPassByValue;
  2.  
  3. procedure DoubleValue(val1: Integer);
  4. begin
  5. val1 *= 2;
  6. end;
  7.  
  8. procedure Main();
  9. var x: Integer;
  10. begin
  11. x := 10;
  12. DoubleValue(x);
  13. WriteLn('The value of x is now... ', x);
  14. end;
  15.  
  16. begin
  17. Main();
  18. end.


Memory Table
Location Value
99 ...
100 ...
101 ...

Routine Table - Main
Variable Name Location
x 100


At program start up we have nothing special in memory. There are no global variables, and the entry point calls the Main procedure on line 18. At this point we can create a Routine Table for Main. The Main procedure declares a variable x. To represent this we add a line in the routine table for x and assign it a free location in memory. In this case we have chosen to locate it at position 100. The program is now ready to execute line 11.


Memory Table
Location Value
99 ...
100 10
101 ...


Line 11 contains the assignment statement x := 10. Using the routine table we see that x is located at position 100 in the Memory Table. Following the instructions we assign the value 10 to the memory location 100 as shown above. Line 12 then calls the DoubleIt procedure, passing a copy of the value of the x variable.


Memory Table
Location Value
99 ...
100 10
101 10
102 ...

Routine Table - DoubleIt
Variable Name Location
val1 101


When the DoubleIt routine is executed it allocates space for its val1 parameter. We create a new Routine Table for this and add the val1 variable to it. The val1 variable is passed by value so we allocate a new space for val1, in this case we have used location 101. The value of the argument x is copied into this location. The resulting tables are shown above.


Memory Table
Location Value
99 ...
100 10
101 20
102 ...


The DoubleIt procedure executes line 5 which doubles the value stored in the val1 variable. We locate val1 at location 101, and double the value stored there. The tables above show the new values stored in memory. When DoubleIt ends its routine table us scrapped, and execution return to line 13.


Through this process the value of x in Main has not changed. As a result line 14 will print the original value 10.

Pass by Reference

Lets have a look at how this works using pass by reference.

program TestPassByReference;
 
procedure DoubleValue(var val1: Integer);
begin
  val1 *= 2;
end;
 
procedure Main();
var x: Integer;
begin
  x := 10;
  DoubleValue(x);
    
  WriteLn('The value of x is now... ', x);
end;
 
begin
  Main();
end.

Memory Table
Location Value
99 ...
100 10
101 ...

Routine Table - Main
Variable Name Location
x 100


The program starts in exactly the same way. We have x located at memory location 100. Main then updates the value of x to be 10. We are not at the point where we are ready to call DoubleIt, line 12.


Memory Table
Location Value
99 ...
100 10
101 ...

Routine Table - DoubleIt
Variable Name Location
val1 100


The difference comes when the argument x is passed to DoubleIt as this parameter is passed by reference. Rather than assigning a new location for val1, it copied the location of the variable passed to it. In this case x is at location 100, so 100 is set as the location for val1.


Memory Table
Location Value
99 ...
100 20
101 ...


The program now continues as normal. The value of val1 is doubled, this time changing the value at location 100. When the routine ends control returns to the Main procedure. It prints the value of x which reads its value from location 100. In this case the value 20 is printed. We can see that when a value is passed by reference the called routine has the ability to change the value of the argument that is passed to it.

Other Parameter Types

Languages may offer you a number of different kinds of parameter passing. These usually include the following options.

  • Pass by Value: The value of the argument is copied and passed to the parameter.
  • Pass by Reference for input and output: The argument location is copied to the parameter. The parameter expects that it has a value it can read. In Pascal this is done using the var keyword.
  • Pass by Reference for output only: The argument location is copied to the parameter. The parameter expects that no value has been set, making it an error to read the value before setting a value. In Pascal this is done using the out keyword.

The difference between the two pass by reference parameter mechanisms is based around the value being passed in. If you want a value to be supplied and returned via a parameter you need it to have a value before the routine it called. For this kind of parameter you use the var keyword in Pascal. It is an error to pass an un-initialised value to this parameter. In other cases you want the routine to set the value of the variable, without needing a value to be passed in. Consider the ReadLn routine, this reads a value from the console and updates the passed in variable. ReadLn does not need you to pass any value in. In these cases you can indicate to the compiler that you do not need a value to be passed in and that the parameter is used to output a value. In Pascal this is done using the out keyword.

Limitations of By Reference parameters

You can pass any expression to a parameter that is passed by value. This is not the case with pass by reference parameters. When you have a pass by reference parameter of any kind you can only pass a variable as the argument. For example you can all DoubleIt(x) but you cannot call DoubleIt(10) or DoubleIt(x + 1).

Conclusion

Passing parameters by reference provides a means of returning values from parameters. This will allow you to return values from procedures, and to return multiple values when needed. Not all languages support passing values by reference, so its important to check to see if your language supports this.

Language Support
Language Support Default Comments
Pascal Yes By value var for in/out, out for out
C No By value Pass by reference is done manually by passing pointers
C++ Yes By value Using "int &x"
Objective C No By value Pass by reference is done manually by passing pointers
Java No By value. Does not support passing by reference. Also see Value and Reference Types
C# Yes By value ref for in/out, out for out
Visual Basic .NET Yes By value ByRef for in/out
Visual Basic Yes By reference ByVal to pass by value


Article by Andrew Cain