Procedures and Control Flow CS351 – Programming Paradigms Recap Recall the various methods of control flow: Sequencing, Selection, Iteration etc. Subroutines (Methods) can also be used to implement some form of control flow. However subroutines not only abstract the idea of control but also data ie a subroutines is an abstraction to perform a well defined operation. Subroutines These are the most common control abstraction in most programming languages. Some point in the program, known as the caller calls the subroutine and then waits for it to finish. Most subroutines are parameterised: Arguments passed are actual parameters Arguments used to define a subroutine are called formal parameters. Subroutines cont… A subroutine that returns a value is called a function. A subroutine that does not return a value is called a procedure. We will look at: Calling subroutines Parameter Passing Variable Argument lists Generic Methods (via Java and C++) Calling Subroutines SP Temps. D SP Args. FP Local Vars. C Misc. Return addr B A FP The main method calls subroutine A. Subroutine A then calls B, which calls C. C then calls D. The Frame Pointer FP, is used to tell which is the current active subroutine while the stack pointer SP, tells us where to push new information about the subroutines. Stack Elements Each stack element from the previous slide is known as an activation record. Each activation record takes (at least) the following into account: Arguments and Return values Temporaries Bookkeeping Information Calling Subroutines The caller: 1. saves any registers whose values are needed after the call. 2. computes the values of arguments and moves them into the stack. 3. uses a special subroutine call instruction to jump to the subroutine, while passing the return address to the stack. Calling Subroutines cont… After the subroutine has completed: 1. 2. 3. 4. The return value is moved into a reserved location in the stack. FP and SP are restored. Jump back to the return address. The return value (if any) is moved to whereever it is needed. Optimisations for C/C++ To help overcome the need for stack activation for frequently used subroutines, it is possible to expand the subroutine into the code to speed up the operation. In C/C++ the inline keyword is used to achieve this. inline int max( int a , int b ) { return a > b ? a : b } main() int a = max ( 5 , 6 ); main() a = 5>6 ? 5:6; Parameter Passing Parameters allow subroutines to take arguments. These arguments may control certain aspects of the subroutines behaviour or specify the data on which they operate. We will look at Parameter Passing Modes Default and missing parameters Variable length arguments lists Parameter Passing Modes There are two major type of parameter passing modes: 1. 2. Pass by value Pass by reference Suppose we have some fragment of code P(x); /* x is some global variable */ We wish to pass x as a parameter to subroutine P. Should we provide P with a copy of x or with the address of x? Do the different methods make any difference? Parameter Passing by Value Each actual parameter is assigned into the corresponding formal parameter is called. Once this happens the two are independent and there are no side effects. In Java: void swap(int a, int b) { tmp = a; a = b; b = tmp; } main() { int a = 8; int b = 6; swap(a,b); System.out.println(a+ ‘’ +b); Parameter Passing by Reference Each formal parameter introduces, within the body of the subroutine, a new name for the corresponding actual parameter. This means that the actual parameter and the formal parameter alias the same data or object. In most languages the parameter passed by reference must be an l-value, it cannot be the result of an expression or any value without and address. Parameter Passing by Reference In C/C++: void swap( int a, int b ) { tmp = a; a = b; b = tmp; } main(){ int a = 8; int b = 6; swap(a,b); std::cout<<a<“ “<<b<<std::endl; Answer? 8 6 – Why? We need to tell C++ pass by reference! void swap ( int& a, int& b ) { tmp = a; a = b; b = tmp; } main() { int a = 8; int b = 6; swap ( *a, *b ); Parameter Passing x : int procedure f ( y : int ) y := 3 print x … x:=2 f(x) print x Call by value – answer? Call by reference - answer? References in C++ We have already seen the use of the & reference operator. void swap (int &a, int &b ){ … } In C++, reference parameters can be made to be read only through the use of the const keyword. int * rd_only ( const int& a ) const { return *a; } Another example: int i; int & j = i; i = 2; j = 3; std::cout << i ; Default Parameters It is possible to have subroutines that have default formal parameters in the absence of actual parameters. For example in C++: int f(int = 0); void g() { int a = f(1); // ok int b = f(); // ok, default argument used } Variable Number of Arguments Lisp, Python, C/C++ and Java all support the ability to create subroutines that can accept a variable amount of arguments passed. The printf function in C is defined as follows: int printf( char * format, … ) The three dots, … , is known as an ellipses and is a valid keyword in the language ( and in Java!). The ellipses tells the compiler to expect a variable amount of arguments. Variable Arguments in Java static void print_lines ( String… lines ) { System.out.println(lines.length); for (String s : lines) System.out.println(s); } print_lines(“This is”, “an example”, “of multiple” , “arguments!” ); Doing this in Java is easy, in C/C++ not as easy, we will do some in the lab. Java demands that all of the elided arguments are of the same type Generics On Thursday we will discuss the issue of implementing generic methods in C++ and Java.