Procedure Calls: Communication between the calling and the called procedures via parameters. Parameters are passed using different mechanisms. Parameter passing is the mechanism of substitution of formal parameters by actual parameters. Execution of the body of the procedures is the replacement of procedure invocation with procedure body Parameter passing mechanisms: Call-by-value : evaluate the actual parameters, assign them to corresponding formal parameters, execute the body of the procedure int p(int x) { x = x + 1 ; return x ; } with call-by-value, an expression y = p(5+3) is executed as follows: evaluate 5+3 = 8, call p with 8, assign 8 to x, increment x, return x which is assigned to y. Call-by-reference: evaluate actual parameters, which must have l-values assign these l-values to l-values of corresponding formal parameters, execute the body. int z = 8 ; y = p(z) ; after the call, y and z will both have value 9. Call-by-reference is explicit in C, whereas implicit in C++; for the above program, in C, one has to write it as int p (int *x) { *x = *x + 1 ; return *x ; } ... int z ; y = p (&z) ; Call-by-value-result: works like call by value bu in addition, formal parameters are assigned to actual parameters at the end of procedure with call-by-value-result (which C doesn't support - hence the following is pseudo-code), void p (int x, int y) { x = x + 1 ; y = y + 1 ; } ... int a = 3 ; p (a, a) ; after the call, a will have the value 4, whereas with cally-by-reference, a will have the value 5. the following is the equivalent of call-by-value-result call above: x = a ; y = a ; x = a ; x = x + 1 ; y = y + 1 ; a = x ; a = y ; thus, at the end, a = 4. Call-By-Value -------------- Preprocessing: a) create a block whose body is that of the procedure being called b) introduce declarations for each formal parameter, and intialize them with the values of the actual parameters Inline: Subsitute the block (obtained by the above preprocessing step) in the place of procedure invocation statement. Example: int z; void p(int x) { z = 2*x; } main() { int y; p(y); } Replacing the invocation p(y) in the above manner yields the following main pogram: main() { int y; { /* Created a new block corresponding to body of f */ int x = y; /* Assign acual parameter to formal parameters */ z = 2*x; /* inline body of p as usual. */ } Fine print: We need to ensure that the variables declared in the body of the procedure do not clash with the variable names visible at the invocation point. If they do, the variables in the procedure body whould be renamed to avoid the clash. Call-By-Value-Result ---------------------- In addition to the preprocessing step in CBV, add assignment statements to the end of the block that assigns formal parameter values to actual parameters. Example: For the above program, we wil get: main() { int y; { /* Created a new block corresponding to body of f */ int x = y; /* Assign acual parameter to formal parameters */ z = 2*x; /* inline body of p as usual. */ y = x; /* assign formal parameter value back to actual parameters */ } Call-By-Reference ------------------ Same as the steps for CBV, except that the variables introduced corresponding to formal parameters will be references. With this change, the result of CBR on the above example would be: main() { int y; { /* Created a new block corresponding to body of f */ int& x = y; /* Assign acual parameter to formal parameters */ z = 2*x; /* inline body of p as usual. */ } } Call-by-Name ------------- Preprocessing: substitute formal parameters in the body of the procedure by actual parameter expressions. If any of the actual parameters have the same name as a local variable used in the called procedure body, then the local variable will first be renamed before the substitution. Inline: Substitute the invocation expression with the modified procedure body. Example: In the above example, we will get: main() { int y; { z = 2*y; } } Macros -------- Macros work very much like call-by-name parameter passing, and has some of its problems described below. In addition, macros suffer from the fact they do not support the concept of local variables. This means that possible name clashes between actual parameters and variables in the body of the macro will lead to unexpected results. For instance, given the macro: #define sixtimes(y) { int z; z = 2*y; y = 3*z;} and the program: main() { int x = 5, z = 3; sixtimes(z); } After macro substitution, we get the program: main() { int x = 5, z = 3; { int z; z = 2*z; z = 3*z; } } which is different from what we would have got with CBN parameter passing. In particular, the name confusion between the local variable z and the actual parameter z would have been avoided, leading to the following result: main() { int x = 5, z = 3; { int z1; /* local variable renamed to avoid clash with actual parameter */ z1 = 2*z; z = 3*z1; } } Difficulties in Using Each of the Parameter Passing Mechanisms ---------------------------------------------------------------- CBV: Easiest to understand, no difficulties or unexpected results CBVR: When the same parameter is passed in twice, the end result can differ depending on the order in which values are assigned back to actual parameters. int f(int x, int y) { x = 4; y = 5; } void g() { int z; f(z, z); } If assignment of formal parameter values to actual parameters (on completion of f) were performed left to right, then z would have a value of 5. If they were performed right to left, then z will be 4. Otherwisely, relatively easy to understand. CBR: Aliasing can create problems. For instance, the procedure: int arev(int a[], int b[], int size) { for (int i = 0; i < size; i++) a[i] = b[size-i-1]; } will normally copy b into a, while reversing the order of elements in b. For instance, if b is {1,2,3}, then a will be {3,2,1}. However, if a and b are the same, as in an invocation arev(c,c,4), the result is quite different. If c is {1,2,3,4} at the point of call, then its value on exit from arev will be {4,3,3,4}. CBN: CBN is probably the most complicated of the parameter passing mechanisms, and can be quite confusing in several situations: (a) if the actual parameter is an expression with side-effects: void f(int x) { int y = x; int z = x; } main() { int y = 0; f(y++); } Note that after a call to f, y's value will be 2 rather than 1. (b) if the same variable is used in multiple parameters: void swap(int x, int y) { int tp = x; x = y; y = x; } main() { int a[] = {1, 1, 0}; int i = 2; swap(i, a[i]); } Intuitively, we expect i to be 0 and a to be {1, 1, 2} after the swap. However, the actual result when using CBN is different, as can be found by replacing the call to swap by the body of swap: i will be 0, and a will be {0, 1, 0}. Macro: Macros share all of the problems associated with CBN. In addition, macro substitution does not perform renaming of local variables, leading to additional problems illustrated earlier. Goto Statements =============== Java doesn't have goto; breaking out of deeply nested loops supported via "labelled break" statemnt. e.g., l1 : for ( ... ) { whlie ( ... ) { ... break l1 } } here, break l1 staements causes the control to break out of the for loop Restrictions in use of goto: - jumps outside of a procedure - jumps from outer blocks to inner blocks or unrelated blocks goto l1; if ( ... ) then { int x ; x = 5 ; l1: y = x * x ; } in this case, (if goto is allowed to inner blocks) the value of x is not known when jump is made to l1, since x is allocated only when the block containing it (if statement evaluates to true) is entered, but in this case jump is to middle of the block, so x is not even defined! Similarly, gotos into unrelated blocks is not allowed: if ( ... ) { ... goto l2 ; } else { ... l2 : ... }