Note: in C, C++ and Java both procedures and functions are called "functions". Procedures are characterized by a "void" return type. The "kind" is implicit.
procedure print_square_root(x:integer); //prints the integer approximation of the square root of x // kind procedure, name print_square_root, parameter name x (passed by value), parameter type integer var y: boolean; // declaration of local variable y of type boolean begin // begin procedure body y := 0; while y * y <= x do y := y + 1; write(y-1) end; // end procedure body (and procedure declaration)
void print_square_root(int x){ int y = 0; while (y * y <= x) y = y + 1; cout << y-1; }
function square_root(x:integer):integer; //returns the integer approximation of the square root of x // kind function, name square_root, parameter name x (passed by value), // parameter type integer, function return type integer var y: boolean; begin y := 0; while y * y <= x do y := y + 1; square_root := y - 1 // specification of return value end;
int square_root(int x){ int y = 0; while (y * y <= x) y = y + 1; return y-1; }
print_square_root(10); // prints 3
n := square_root(10) // assigns to n the value 3
n = square_root(10);
In call by value, the actual parameter can be a general expression (it does not need to be a variable).
In call by reference, the actual parameter should be a variable.
void swap(int &x, int &y){ int temp; temp = x; x = y; y = temp; }Example of a call to swap:
int n = 1; int k = 2; swap(n,k); cout << n; // prints 2 cout << k; // prints 1In languages which do not have call by reference, like C, we can simulate it by using pointers called by value. Here is an example of swap implemented in C:
void swap(int *x, int *y){ int temp; temp = *x; *x = *y; *y = temp; }Of course, in this case the call to swap must pass the location addresses of the variables to be swapped:
int n = 1; int k = 2; swap(&n,&k); cout << n; // prints 2 cout << k; // prints 1
Notes: "call by reference" and "call by value using pointers" are similar, but are not the same thing. In particular, when we want to simulate call by reference by using pointers called by value, we must be careful to use the actual parameters always in a dereferenced mode.
Call by reference is actually implemented in C++ by using pointers and automatic deferencing.
C++ allows actual parameters to be generic expressions, but in such case the formal parameters must be constants. When the actual parameter is not a variable, then a temporary variable is created at the moment of the call, the actual parameter is evaluated and its value is stored in the location of the temporal variable. The lifetime of the temporal variable is the activation time of the procedure.
Also in these cases the actual parameters should be variables.
void p(int value-result x, int value-result y){ x = x + 1; y = y + 1; }The following is an example of a call to p:
int z = 0; p(z,z); cout << z; // prints 1Note that if the parameters were passed by reference, then the same fragment of code would have printed the value 2. This is because in the call p(z,z), x and y (and z) would be aliases for the same location.
A strict operator always causes the evaluation of its parameters. Conversely, a non-strict operator may or may not evaluate them, depending on the situation. An example of non-strict operator is the "logical or" of C++ (||), which evaluates the second argument only if the first is false. The logical or of Pascal, on the contrary, is strict.
if (x==0 || 1/x > 0.3) ... // C++ if (x=0 or 1/x > 0.3) ... // PascalIf x = 0, the Pascal instruction will give error (floating exception: division by 0) because 1/x > 0 is evaluated despite the first expression is true. The C++ instruction, on the contrary, will not give error: the evaluation of the condition stops after the evaluation of the first argument and returns true.
Note that in both call by need and call by name the actual parameter may be generic expressions (it's the all point, in fact).
bool or(bool name a, bool name b){ if (a) return true; else return b; }Example of a call:
if (or(x==0 , 1/x > 0.3)) ... // when x = 0 the condition evaluates to true
Note: Call by value cannot be used to implement non-strict operators, because the parameters are evaluated at the moment of the call (even if not needed). Call by reference or call by value-result cannot be used either, because the actual parameters (in general) must be variables. Note that even in case the language allow to use generic expressions in call by reference (like C++), they are evaluated first and stored in temporary variables. Hence the non-strictness effect cannot be achieved.
bool or(const bool &a, const bool &b){ if (a) return true; else return b; }Example of a call:
if (or(x==0 , 1/x > 0.3)) ... // when x = 0 the condition gives error