Snag: passing an array as a value parameter is slow (because all the
elements have to be copied, unless the implementor can optimise the copy
away, or unless s/he cheats).
This example of passing array parameters by value in Pascal should output
1, but if it outputs a -1 the implementor has not bothered to
copy the array parameter:
type arr = array [1..2] of integer;
var a: arr;
procedure nasty (c: arr);
begin
a[1]:= -1;
writeln (c[1]);
end;
begin
a[1]:= 1;
nasty (a);
end.
Many languages provide both call by reference and call by value.
If a language provides call by reference but not call by value, there is a problem if we want to use an expression as an actual parameter. Some languages just forbid this, others place the value of the expression into a dummy argument, and the address of the dummy argument is passed for use as the formal-parameter (thus assignments to the dummy argument work, but they do not affect any variables outside).
Snag: call by reference usually has to be more strongly type-checked than call by value.
This Fortran program distinguishes between call by reference and call by copy/copy back:
PROGRAM TEST
COMMON I
I = 1
CALL P(I)
IF (I .EQ. 4) THEN
PRINT *, 'call by reference'
ELSE
IF (I .EQ. 3)
PRINT *, 'call by copy/copy back'
ENDIF
ENDIF
END
SUBROUTINE P(J)
COMMON I
J = 2
J = I + J
END
Snag: although fields of packed records can now be passed without
difficulty, passing arrays is as slow as in call by value.
Problem: suppose that an actual-parameter is a[i], and suppose that
i=4 at the time of the call. Suppose that the procedure body assigns
a new value to the formal-parameter and also contrives to change the value
of i to 5. Is the new value written back to a[4] or to a[5]?
Most languages that provide call by copy/copy back also provide call by value.
As with call by reference, if a language provides call by copy/copy back but not call by value, there is a problem if we want to use an expression as an actual parameter. The simplest solution is just to omit the copy back.
Some languages provide call by value but not call by reference or by copy/copy back. If the language provides pointers to named variables, then call by reference can be simulated by passing pointers by value.
Thus languages that do not allow pointers to named variable, normally have to provide call by reference or by copy/copy back.
double integrate (double start, double finish, double(*f)(double))
{
. . . f(x) . . .
}
. . .
area= integrate (0.0, 1.0, sqrt);
If an actual parameter is a variable, this is equivalent to call by reference. However, if the actual parameter is an expression, the consequences can be surprising e.g.
real procedure series (i, k, term)
value k; integer i, k; real term;
begin real sum;
sum := 0.0;
for i := 1 step 1 until k do
sum := sum + term;
series := sum;
end;
integer j;
print (series (j, 100, 1/j));
In this case the last parameter is implemented in a similar way to call by
procedure, by constructing a small function (known as a thunk). The
thunk is called at all the places in the body of series where that
actual-parameter was supposed to be replaced.
Generic (parameterised polymorphic): same operation can act on different types
The difference is only obvious to the implementor e.g. if you have to write code for each of a limited number of different versions, rather than one (or a few) piece(s) of code that can cope with all possible parameter types.
This facility is particularly associated with Abstract Data Types
(
18), where both a type and its operations may be polymorphic.