call by value: copy going into the procedure
call by result: copy going out of the procedure
call by value result: copy going in, and again going out
call by reference: pass a pointer to the actual parameter, and indirect through the pointer. If the actual parameter is a variable or an array element (not an expression), then the procedure can assign to the formal parameter and as a result assign into the actual parameter as well. (The more general programming language term for "variable or array element" or the like is l-value, in other words, something that can be on the left hand side of an assignment statement.)
call by name: re-evaluate the actual parameter on every use. For actual parameters that are simple variables, this is the same as call by reference. For actual parameters that are expressions, the expression is re-evaluated on each access. It should be a runtime error to assign into a formal parameter passed by name, if the actual parameter is an expression. One possible implementation: use anonymous function ("thunk") for call by name expressions. (This is traditionally used to implement call by name in languages such as Algol and Simula.)
Call by name is not supported in recent languages, but is still an important concept in programming language theory. (In newer languages, if you want the effect of call by name, pass a procedure as a parameter.)
For a pure functional language, lazy evaluation has exactly the same semantics as call by name, but will generally be more efficient. With lazy evaluation, the actual parameter is not evaluated until the formal parameter's value is needed. At that time, the actual parameter is evaluated, and the value is cached. Then, if the value of the formal parameter is needed again, the cached value will be used. (This is safe in a pure functional language, since the value of an expression will always be the same each time it is evaluated.) Thus, with lazy evaluation, an actual parameter is evaluated either 0 or 1 times.
Call by value is particularly efficient for small pieces of data (such integers), since they are trivial to copy, and since access to the formal parameter can be done efficiently. Java uses call by value for primitive data types.
Call by reference is particularly efficient for large pieces of data (such as large arrays), since they don't need to be copied.
Fortran uses call by reference
Insecurity in early FORTRANs -- passing a constant allowed the procedure
to change the constant!
Algol 60 has call by name, call by value
Ada uses different designations: IN
, OUT
,
IN OUT
:
IN
is the same as
call by value, OUT
is the same as call by result, and IN
OUT
is the same as call by value result. In addition,
IN
parameters are local constants -- you can't assign into them.
An important related concept: aliasing. Two variables are aliased if they refer to the same storage location. A common way that aliasing can arise is using call by reference. A formal parameter can be aliased to a nonlocal variable, or two formal parameters can be aliased.
begin integer n; procedure p(k: integer); begin n := n+1; k := k+4; print(n); end; n := 0; p(n); print(n); end;Note that when using call by reference,
n
and k
are aliased.
Output:
call by value: 1 1 call by value-result: 1 4 call by reference: 5 5
begin integer n; procedure p(k: integer); begin print(k); n := n+10; print(k); n := n+5; print(k); end; n := 0; p(n+1); end;Output:
call by value: 1 1 1 call by name: 1 11 16
This example illustrates assigning into a parameter that is passed by reference or by name
begin array a[1..10] of integer; integer n; procedure p(b: integer); begin print(b); n := n+1; print(b); b := b+5; end; a[1] := 10; a[2] := 20; a[3] := 30; a[4] := 40; n := 1; p(a[n+2]); new_line; print(a); end;
Output:
call by reference: 30 30 10 20 35 40 call by name: 30 40 10 20 30 45
Passing a primitive type as a parameter:
class PassByValue { public static void main(String[] args) { double x = 1.0; System.out.println("before: x = " + x); halveIt(x); System.out.println("after: x = " + x); } public static void halveIt(double arg) { arg = arg/2.0; System.out.println("halved: arg = " + arg); } }Sample output:
before: x = 1.0 halved: arg = 0.5 after: x = 1.0Here is another example, in which we pass an object as a parameter.
import java.awt.Point; class PassObject { public static void main(String[] args) { Point p = new Point(10,20); System.out.println("before halveX: p.x = " + p.x); halveX(p); System.out.println("after halveX: p.x = " + p.x); setIt(p); System.out.println("after setIt: p.x = " + p.x); } public static void halveX(Point q) { q.x = q.x/2; System.out.println("halved: q.x = " + q.x); } public static void setIt(Point q) { q = new Point(0,0); System.out.println("setIt: q.x = " + q.x); } }Sample output:
before halveX: p.x = 10 halved: q.x = 5 after halveX: p.x = 5 setIt: q.x = 0 after setIt: p.x = 5