511 likes | 596 Views
Functions. Functions, locals, parameters, and separate compilation. What is a Function?. A function is a named block of code Type: the type of the return value it computes Input: parameters that are passed to the function
E N D
Functions Functions, locals, parameters, and separate compilation
What is a Function? • A function is a named block of code • Type: the type of the return value it computes • Input: parameters that are passed to the function • Output: its return value computed based on input parameters and maybe other stuff • Local variables: variables declared inside the function block
Typical Function type name input int max(int a, int b) { int maxval; if (a > b) maxval = a; else maxval = b; return maxval; } { local variable code block output
Exercise 1 • Write the max function as given • Use forward declaration for max() • Write a similar min function • Write a little main function to • Declare two ints • Ask for user input to set their values • Call max and min • Print out the inputs, then their max and min (with appropriate indications) • Compile and test your program
What is a Function? • Recall the two parts of a function: • Function header • Function body (or definition) • Recall function header: • Type: the type of the return value • Name: name of the function • Formal parameter list: the input parameters with their types
Function Signature, Body • Function signature is: • Name • Formal parameter type list • This allows compiler and linker to figure out which code to invoke • Function body (or definition) • Block of code, may have local vars • Has return statement for return value
What is a Function? • Function generally has: • Input: parameters that are passed to the function (may be null list) • Output: its return value computed based on input parameters (if any) and maybe other stuff • Local variables: variables declared inside the function block – generally these go away on function return
How Does a Function Pass Parameters? • Function names input parameters • These are “formal parameters” that may be referenced as variables inside the function • The scope of these variables is confined to the function code block • When invoked, expressions are used • These are “actual parameters” • Formal parameters are “bound” to them
Another Function int sum(int a, int b) { int sum = 0; for (int i = a; i <= b; ++i) sum += i; return sum; }
Exercise 2 • Write the sum function as given • Write a little main function to • Declare two ints X and Y • Ask for user input to set their values • Print out the inputs, then call sum and print out the sum from X to Y inclusive • Compile and test your program • What could possibly go wrong? • Stand up, walk around, talk about it
Analysis • What could go wrong with the sum function as written? • What if the actual parameter passed in as a is greater than the actual parameter passed in as b? • Did you test that case? • How do we fix it?
Exercise 3 • Use the max and min functions from before to fix the sum function • Compile and test • Did you apply max and min from inside sum, or before you called sum? • Which way is “safer”? Why?
Separate Compilation • A way to make the development process more efficient and manageable • Typically three kinds of files: • Main program file – has main() • Header file(s) – has declarations, included in main (and elsewhere) • Implementation file(s) – has function definitions
Exercise 4 • Make a header file with declarations for max, min, and sum • Make a main (or driver) file with only the main() function - #include your header file • Make an implementation file with the function definitions only • Compile impl files with -c option • Compile main with impl.o files
Example $ ls mainSum.cpp minMaxSum.h minMaxSum.cpp $ g++ -c minMaxSum.cpp $ g++ -c mainSum.cpp $ g++ mainSum.o minMaxSum.o $ ./a.out ...
Analysis • What is going on here? • Compiler produces “relocatable object” code that has information needed for linking • What references are dangling • What references it can bind • Linker can then link these .o files to make an a.out executable file
Why Bother? Exercise 5 • Take your old file that had main that called max and min and make it into a driver only (no max or min function definitions in it) – just #include the header file you just made • Now compile your new main with the minMaxSum.o file • Test it out!
Reflection • So now you are able to define and compile useful functions separately from the code that invokes them! • What are some benefits of this? • Only have to recompile the files that change (directly or indirectly) • Can have team members each work on their own file(s)
Hey, I Know a Better Way... • Look back at the sum function • Is there a better way to compute the same value? • Euler did this as a schoolchild • sum(a, b) = count x average = (b – a + 1) * (a + b) / 2
Exercise 6 • Take your old impl file with the definition of sum() in it and change it to compute the sum the Euler way • Now compile your new impl with the -c option • Now link your old main.cpp to the new relocatable object file • Test it out!
Reflection • The header did not change • The interface to the functions did not change • The result of the computation did not change • So we did not need to recompile the driver or change the header!
More Reflection • However, even though only the sum() function changed, we had to recompile the whole impl file with max() and min() in it • What if each of these took 10 minutes to compile? • Better still to separate out each function in its own impl file and compile each separately
Exercise 7 • Split the impl file into three files – one each for sum, max, and min • Note that you will need the header file (at least in sum.cpp) • Now compile them with the -c option • Now link your old main.cpp to the new relocatable object files • Test it out!
(Same) Reflection • The header did not change • The interface to the functions did not change • The result of the computation did not change • So we did not need to recompile the driver or change the header!
How Does a Function Pass Parameters? • There are several ways a function may pass parameters • By value • By reference • By copy-in-copy-out • By pointer (sometimes also called reference) • By name
Call-by-Value • This is the most common way to pass parameters • Space for the type declared in the formal parameter list is allocated on the stack • The value of the expression used at invocation is stored into that place on the stack
Call By Value void swap(inta, int b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); }
Call By Value void swap(inta, int b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b a 2 3
Call By Value void swap(inta, int b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b b a a 2 3 2 3
Call By Value void swap(inta, int b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b b a a temp 2 3 2 3 2
Call By Value void swap(inta, int b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b b a a temp 2 3 3 2 3 2 3
Call By Value void swap(inta, int b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b b a a temp 3 2 3 2 2 3 2
Call By Value void swap(inta, int b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b b a a temp 2 3 2 3 2
Call-by-Reference Using Pointers • This is used for modifying the inputs • Space for pointer only is allocated on the stack • The pointer must be dereferenced to use value • Can change the original object
Call By Reference void swap(int* ints, int i_1, int i_2) { int temp = ints[i_1]; ints[i_1] = ints[i_2]; ints[i_2] = temp; } publicvoid main() { int[] ints= {1, 2, 3, 4}; swap(ints, 0, 3); } ints 4 2 1 3
Call By Reference void swap(int* ints, int i_1, int i_2) { int temp = ints[i_1]; ints[i_1] = ints[i_2]; ints[i_2] = temp; } publicvoid main() { int[] ints= {1, 2, 3, 4}; swap(ints, 0, 3); } ints ints 4 2 1 3
Call By Reference void swap(int* ints, int i_1, int i_2) { int temp = ints[i_1]; ints[i_1] = ints[i_2]; ints[i_2] = temp; } publicvoid main() { int[] ints= {1, 2, 3, 4}; swap(ints, 0, 3); } ints temp ints 4 2 1 3 1
Call By Reference void swap(int* ints, int i_1, int i_2) { int temp = ints[i_1]; ints[i_1] = ints[i_2]; ints[i_2] = temp; } publicvoid main() { int[] ints= {1, 2, 3, 4}; swap(ints, 0, 3); } ints temp ints 4 4 4 2 1 3 1
Call By Reference void swap(int* ints, int i_1, int i_2) { int temp = ints[i_1]; ints[i_1] = ints[i_2]; ints[i_2] = temp; } publicvoid main() { int[] ints= {1, 2, 3, 4}; swap(ints, 0, 3); } ints temp ints 1 1 4 4 2 3 1
Call By Reference void swap(int* ints, int i_1, int i_2) { int temp = ints[i_1]; ints[i_1] = ints[i_2]; ints[i_2] = temp; } publicvoid main() { int[] ints = {1, 2, 3, 4}; swap(ints, 0, 3); } ints temp ints 1 1 4 4 2 3 1
Function Calls • Additionally, using the & operator (instead of a *) will make that parameter call-by-reference. • It will hide the obtained address, but still work with and alter the same object/variable.
Call By Reference (2) void swap(int &a, int &b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); }
Call By Reference (2) void swap(int &a, int &b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b a 2 3
Call By Reference (2) void swap(int &a, int &b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b b a a 2 3
Call By Reference (2) void swap(int &a, int &b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b b a a temp 2 2 3
Call By Reference (2) void swap(int &a, int &b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b b a a temp 3 2 3 3 2
Call By Reference (2) void swap(int &a, int &b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b b a a temp 3 3 2 2 2
Call By Reference (2) void swap(int &a, int &b) { int temp = a; a = b; b = temp; } void main() { int a = 2; int b = 3; swap(a, b); } b b a a temp 3 3 2 2