Beginner's Guide to Passing Parameters
Overview
No programming language course would be compete without a discussion of pass-by-value and pass-by-reference. Understanding the difference between the two is important in order to understand the engineering tradeoffs that go into the creation of a programming language, as well as to write correct, bug-free code based on accurate assumptions about how the language works.
Parameter Passing Techniques
There are a number of different ways a programming language can pass parameters:
- Pass-by-value
- Pass-by-reference
- Pass-by-value-result
- Pass-by-name
The most common are pass-by-value and pass-by-reference. C++ supports both techniques, while most other languages (Java, JavaScript, Python, etc.) use pass-by-value exclusively.
Pass-by-value
Consider the following pseudo-code:
AssignTo(param):
param = 5
Main:
x = 3
AssignTo(x)
print x
Or, equivalently, the real C++ code (using pass-by-value):
#include <iostream>
#include <cstdlib>
void AssignTo(int param) {
param = 5;
}
int main(int argc, char* argv[]) {
int x = 3;
AssignTo(x);
std::cout << x << std::endl;
return 0;
}
In a pass-by-value system, the statement AssignTo(x)
creates a copy of the argument x
. This copy has the same value as the original argument (hence the name "pass-by-value"), but it does not have the same identity. Thus the assignment within AssignTo
modifies a variable that is distinct from the supplied parameter. Thus, the output in this passing style is 3
and not 5
.
Pass-by-reference
Let's consider the same example pseudo-code once again:
AssignTo(param):
param = 5
Main:
x = 3
AssignTo(x)
print x
Or, equivalently, the real C++ code (using pass-by-reference):
#include <iostream>
#include <cstdlib>
void AssignTo(int& param) { // use of ampersand (&) implies reference
param = 5;
}
int main(int argc, char* argv[]) {
int x = 3;
AssignTo(x);
std::cout << x << std::endl;
return 0;
}
In a pass-by-reference system, the function invocation does not create a separate copy of the argument; rather, a reference to the argument (hence the name "pass-by-reference") is supplied into the AssignTo
function. Thus, the assignment in AssignTo
modifies not just the variable named param
, but also the variable x
in the caller, causing the output to be 5
and not 3
.
Implicit Pointers
In some programming languages (such as Java, JavaScript, C#, etc.), parameters of an object type are implicitly pointers. This does not mean the parameter passing style is pass-by-reference. Pass-by-value and pass-by-reference can only be distinguished on the basis of assignment; both pass-by-value and pass-by-reference can both modify the supplied parameter (e.g. if the parameter is a pointer, the data that it points to may be modified by the function), but only pass-by-reference can change where the supplied pointer points (whereas pass-by-value cannot reassign the supplied pointer to point to a different address).
Pass-by-value-result
Pass-by-value-result is not commonly built into programming languages directly, but is frequently used in parallel programming. In pass-by-value-result, it is possible to assign to the parameter, but the assignment is delayed. That is, pass-by-value-result invokes the function in a manner similar to pass-by-value; however, at the very end, instead of discarding the copies of the variables, these copies are written back to the original arguments at the very end of the function (allowing tor assignments to affect the caller).
To make this more concrete, consider the following pseudo-code:
AddTo(input1, input2, output):
output += input1
output += input2
Main:
sum = 1
AddTo(sum, sum, sum)
print sum
This is a scenario involving parameter "aliasing"; that is, one parameter actually references another parameter. In pass-by-reference, this aliasing causes the second statement to add a value of 2
rather than a value of 1
, leading to a final result of 4
. In pass-by-value-result, on the other hand, both statements add a value of 1
, resulting in the code printing 3
.
Pass-by-name
In most programming languages, passing an expression as an argument causes the expression to be evaluated, and then the result of that evaluation is passed as an argument to the function. In pass-by-name, rather than pass the result of evaluating the expression, the expression is passed literally to the function. Within the execution of the function, the use of the parameter is replaced by the expression that was provided. Pass-by-name can be thought of as a symbolic or textual substitution.
To make this more concrete, consider the following pseudo-code:
Sum(x, y) = x + y
Times(x, y) = x*y
Main:
print Times(Sum(1, 2), Sum(3, 4))
Under pass-by-value, this would be executed roughly as follows:
var0 = Sum(1, 2) = 1 + 2 = 3
var1 = Sum(3, 4) = 3 + 4 = 7
var2 = Times(var0, var1) = var0 * var1 = 3 * 7 = 21
print var2 // print 21
Under pass-by-name, however, this ends up getting executed roughly as follows:
var0 = Sum(1, 2) = "1 + 2"
var1 = Sum(3, 4) = "3 + 4"
var2 = Times("1 + 2", "3 + 4") = "1 + 2*3 + 4"
var3 = evaluate(var2) = evaluate("1 + 2*3 + 4") = 1 + 6 + 4 = 11
print var3 // print 11
In other words, pass-by-name substitutes the parameters, first, and only then evaluates; other parameter passing styles evaluate parameters first, and only then perform substitution and evaluation. The C and C++ preprocessor is an example of a real-world programming language mechanism that uses pass-by-name.
Conclusion
There are many different ways that programming languages can bind function parameters to the arguments at the point of invocation. Pass-by-value and pass-by-reference are common strategies, but others exist. Hopefully, at this point, you will feel comfortable knowing the difference between these techniques, how to create a simple test case for figuring out the parameter passing style of your favorite programming language, and how to ace your next programming language test.
I use long FileNames to pass control parameters… Up to 250 characters.
Way better than the old 8 chr limit…
Video files can contain the start point for play, duration, speed and a lot more.