Functions
Tian-Li Yu
[email protected]
Department of Electrical Engineering
National Taiwan University
Objectives
➢ Understand functions
⚫ create
⚫ invoke
⚫ pass parameters to
➢ Pass-by-value, pass-by-reference
➢ Function overloading
➢ Function prototypes & header files
➢ Scope of local and global variables
➢ Inline functions
2
Introducing Functions
➢ A function is a collection of statements that are
grouped together to perform an operation
3
Introducing Functions (contd.)
➢ Function signature is the combination of the
function name and the parameter list.
➢ The variables defined in the function header →
formal parameters.
➢ When a function is invoked, you pass a value to
the parameter → actual parameter (argument).
➢ A Function may return one value or void.
4
Example: max
#include <iostream>
using namespace std;
/** Return the max between two
numbers */ int main() {
int max(int num1, int num2) { int i = 5;
int result; int j = 2;
if (num1 > num2) int k = max(i, j);
result = num1; cout << "The maximum is " << k;
else return 0;
result = num2; }
return result;
}
5
Struct
➢ If you need to return more than one variable.
⚫ You may use struct.
⚫ But later we’ll learn class in C++, which is easier to
use.
Complex add(Complex c1, Complex c2) {
struct Complex { Complex result;
double re; result.re = c1.re + c2.re;
double im; result.im = c1.im + c2.im;
}; return result;
}
6
Calling Functions
7
Calling Functions (contd.)
i is now 5
8
Calling Functions (contd.)
j is now 2
9
Calling Functions (contd.)
Invoke max(i, j)
10
Calling Functions (contd.)
invoke max(i, j)
Pass the value of i to num1 (num1=5)
Pass the value of j to num2 (num2=2)
11
Calling Functions (contd.)
return result, which is 5
12
Calling Functions (contd.)
Assign the return value to k
int k = 5;
13
Call Stacks
14
Trace Call Stack
15
Trace Call Stack (contd.)
16
Trace Call Stack (contd.)
17
Trace Call Stack (contd.)
18
Trace Call Stack (contd.)
19
Trace Call Stack (contd.)
20
Trace Call Stack (contd.)
21
Trace Call Stack (contd.)
22
Trace Call Stack (contd.)
23
Pass By Value
➢ When you invoke a function with a parameter,
the value of the argument is passed to the
parameter. This is referred to as pass-by-value.
➢ The actual parameters are not affected,
regardless of the changes made to the formal
parameter inside the function.
➢ If you don’t need a function to return any value,
declare it as void.
TestPassByValue
24
Pass By Value (contd.)
25
Reference Variables
➢ C++ provides a special type of variable, called a
reference variable.
➢ A reference variable is an alias for another
variable. Any changes made through the
reference variable are actually performed on the
original variable.
➢ To declare a reference variable, place & in front
of the name.
26
Pass By Reference
➢ Reference variable example
int count = 1;
int &ref_Count = count;
++ref_Count;
cout << "count is " << count << endl;
cout << "ref_Count is " << ref_Count << endl;
➢ We can use reference to modify the actual
parameters in a function.
TestPassByReference
27
Note
➢ Reference is like an alias.
➢ Reference exists only when the variable which it
refers to exists.
➢ For example, this is invalid.
int & count ;
28
Overloading Functions
➢ The max function that was used earlier works
only with the int data type.
➢ What if we need to find which of two floating-
point numbers has the maximum value?
➢ Create another function with the same name but
different parameters.
TestFunctionOverloading
29
Ambiguous Invocation
➢ Sometimes there may be two or more possible
matches for an invocation of a function, but the
compiler cannot determine the most specific
match.
➢ Ambiguous invocation: a compilation error.
30
Ambiguous Invocation (contd.)
int maxNumber(int num1, double num2) {
return (num1 > num2)? num1: num2;
}
double maxNumber(double num1, int num2) {
return (num1 > num2)? num1: num2;
}
int main() {
cout << maxNumber(1, 2) << endl;
return 0;
}
31
Function Prototypes
➢ Like variables, a function int f3(int num) {
needs to be declared if (num == 0) return 0;
return f1(num-1);
before it can be used.
}
➢ One way to ensure it is int f2(int num) {
to place the declaration if (num == 0) return 0;
before all function calls. return f3(num-1);
}
➢ What if… int f1(int num) {
if (num == 0) return 0;
return f2(num-1);
}
32
Function Prototypes (contd.)
➢ The other way is to declare a function prototype
before the function is called.
➢ A function prototype is a function declaration
without implementation. The implementation can
be given later in the program.
33
Function Prototypes (contd.)
int f3(int num);
int f2(int num);
int f1(int num);
int f3(int num) {
if (num == 0) return 0;
return f1(num-1);
}
int f2(int num) {
if (num == 0) return 0;
return f3(num-1);
}
int f1(int num) {
if (num == 0) return 0;
return f2(num-1);
34
}
Function Prototypes (contd.)
➢ You don’t even need to name the parameters in
function prototypes.
➢ But naming parameters is recommended for
better readability.
// Return (base^exponent)
double pow(double base, double exponent);
double pow(double, double);
35
Default Arguments
➢ C++ allows you to declare functions with default
argument values. The default values are passed
to the parameters when a function is invoked
without the arguments.
DefaultArgumentDemo
36
Example: Root Finding By Bisection
➢ Recall our root finding program.
➢ Now let’s make use of function.
➢ DBL_EPSILON
⚫ #include <cfloat> (for C++) or <float.h>
⚫ It’s the minimal difference from 1 that a double can
represent.
BisectionF
37
Recursive Call
➢ Golden rule
⚫ Base case first
⚫ Then recursive call
38
Example: Factorial
int factorial(int num){
int result = 1;
Loop Version for (int i=1; i<=num; i++)
result *= i;
return result;
}
int factorial(int num){
if (num == 0) return 1;
Recursion Version return num * factorial(num-1);
}
39
Tracing Factorial
40
Do Not Abuse Recursion
➢ Fibonacci
⚫ a0 = 0, a1 = 1.
⚫ an = an-1 + an-2
int fibonacci(int x) {
if (x==0) return 0;
if (x==1) return 1;
return fibonacci(x-2)+ fibonacci(x-1);
}
➢ This is bad. Why?
⚫ How many function calls for fibonacci(30)?
41
Tracing Fibonacci
42
Do Not Abuse Recursion (contd.)
➢ Fibonacci int fibonacci(int x) {
⚫ a0 = 0, a1 = 1. if (x==0) return 0;
⚫ an = an-1 + an-2 . if (x==1) return 1;
int a = 0, b = 1;
➢ Loop version for (int i = 2; i <= x; i++) {
result = a + b;
a = b;
➢ Only 30 b = result;
iterations for }
fibonacci(30) return result;
}
43
Practices
GCD
Double^Int
44
Header Files
➢ To make your function reusable, you need to
create a header file (*.h)
➢ A header file define the function prototype.
➢ Other programs use #include to use your
functions.
➢ Function implementations can be in separated
cpp files (recommended) or in the header file.
45
Header File Examples
➢ MyLib.h bool isEven(int number) {
return (number % 2 == 0);
}
#include <iostream>
➢ UseMyLib.cpp #include "MyLib.h"
using namespace std;
int main() {
Note!
cout << isEven(4) << endl;
cout << isEven(5) << endl;
return 0;
}
46
Header File Examples 2
➢ MyLib.h bool isEven(int number);
➢ MyLib.cpp #include <iostream>
#include "MyLib.h"
bool isEven(int number) {
return (number % 2 == 0); using namespace std;
} int main() {
cout << isEven(4) << endl;
cout << isEven(5) << endl;
return 0;
➢ UseMyLib.cpp →→ }
➢ MyLib.cpp and
UseMyLib.cpp in one project.
47
Keyword: #define
➢ Preprocessors: #define, #ifdef, #ifndef, #endif
➢ Note: they are NOT statements.
➢ #define is just a text replacement.
➢ #define N 4
int b = a * N; int b = a * 4;
➢ #define square(x) x*x
int b = square(a); int b = a * a; 48
Keyword: #define (contd.)
➢ #define max(x,y) (x>y)?x:y
int c = max(a, b); int c = (a>b)?a:b;
➢ Why do we need functions then?
⚫ How about #define square(x) x*x
int c = square(3+4); int c = 3+4*3+4; int c = 19;
⚫ How about #define square(x) (x)*(x)
int c = square(3+4); int c = (3+4)*(3+4); int c = 49;
int c = square(++a); int c = (++a)*(++a); ?
49
Keyword: #define (contd.)
➢ In general, use #define as functions is bad.
➢ Why do we need it?
➢ Prevent header files from being included twice:
#ifndef _MY_LIB_H_ Unique identifier
#define _MY_LIB_H_
MyLib.h
bool isEven(int number);
#endif
➢ Recommended way to write a header file. 50
Scope of Local Variables
➢ Local variable: defined inside a function.
➢ Scope: the part of the program where the
variable can be referenced.
➢ The scope of a variable
⚫ Starts from its declaration
⚫ Continues to the end of the block that contains the
variable.
51
Scope of Local Variables (contd.)
➢ Variable declared in the initial action part of a for
loop header has its scope in the entire loop.
➢ Variable declared inside a for loop body has its
scope limited in the loop body from its
declaration and to the end of the block that
contains the variable.
52
Scope of Local Variables (contd.)
53
Scope of Local Variables (contd.)
54
Global Variables
➢ Global variables are declared outside all functions
and are accessible to all functions in its scope.
➢ Local variables do not have default values, but
global variables are defaulted to zero.
➢ Notes:
⚫ Do not rely on “defaulted to zero.” (Always initialize)
⚫ Using global variables is usually not recommended.
VariableScopeDemo
55
Unary Scope Resolution
➢ If a local variable name is the same as a global
variable name, you can access the global variable
using ::globalVariable.
➢ The :: operator is known as the unary scope
resolution.
int v1 = 10;
int main() {
int v1 = 5;
cout << "local variable v1 is " << v1 << endl;
cout << "global variable v1 is " << ::v1 << endl;
return 0;
}
56
Static Local Variables
➢ After a function completes its execution, all its
local variables are destroyed.
➢ Sometimes, it is desirable to retain the value
stored in local variables so that they can be used
in the next call.
➢ C++ allows you to declare static local variables.
➢ Static local variables are permanently allocated in
the memory for the lifetime of the program. To
declare a static variable, use the keyword static.
StaticVariableDemo
57
Function Abstraction
➢ You can think of the Function body as a black box
that contains the detailed implementation for the
Function.
58
Benefits of Functions
• Write a Function once and reuse it anywhere.
• Information hiding. Hide the implementation
from the user.
• Reduce complexity.
59
Math Functions
60
Divide and Conquer
➢ Also known as stepwise refinement.
➢ Problem decomposition.
➢ Big, difficult problem → small, easy subproblems.
PrintCalendar
61
Design Diagram
printMonthName
62
Implementation: Top-Down
➢ Stubs can be used for the Functions waiting to be
implemented.
➢ A stub is a simple but incomplete version of a
Function.
➢ The use of stubs enables you to test invoking the
Function from a caller.
void printMonth(int year, int month) {
cout << month << " " << year << endl;
}
63
Implementation: Bottom-Up
➢ Implement one function in the structure chart at
a time from the bottom to the top.
➢ For each function implemented, write a test
program to test it.
➢ Both top-down and bottom-up functions are fine.
➢ Both approaches implement the functions
incrementally and help to isolate programming
errors and makes debugging easy.
➢ Sometimes, they can be used together.
64
Inline Functions
➢ Implementing a program using functions makes
the program easy to read and easy to maintain,
but function calls involve runtime overhead.
➢ C++ provides inline functions to avoid function
calls.
➢ Inline functions are not called; rather, the
compiler copies the function code in line at the
point of each invocation.
65
Inline Function Example
#include <iostream>
using namespace std;
inline void f(int month, int year) {
cout << "month is " << month << endl;
cout << "year is " << year << endl;
}
int main() {
int month = 10, year = 2008;
f(month, year);
return 0;
}
66
Final Notes About Inline
➢ Inline functions are desirable for short functions,
but not suitable for long functions.
➢ long inline functions will dramatically increase the
executable code size when it is copied in multiple
places.
➢ C/C++ allows the compilers to ignore the inline
keyword if the function is too long.
67
Summary
➢ Functions, prototypes, header files.
➢ Pass-by-value, pass-by-reference.
➢ Scope of local, global, and static local variables.
➢ Inline functions.
➢ Divide-and-conquer methodology.
68