Module 5 - Guidance Notes
Module 5 - Guidance Notes
Functions
ENGG1340 COMP2113
Computer Programming II Programming Technologies
2
TOP-DOWN DESIGN (DIVIDE
AND CONQUER) APPROACH
3
Top-Down Program Design
• A good way to design a program is to break down the
task to be accomplished into a few sub-tasks
• Each sub-task can be further decomposed into
smaller sub-tasks, and this process is repeated until
all sub-tasks are small enough that their
implementations become manageable
• This approach is called top-down design (a.k.a. divide
and conquer)
4
Top-Down Design
• Example: Compute the final score for a student
Simple GPA Calculator
5
Functions
• Preserving the top-down design structure in a program
will make it easier to understand and modify the program,
as well as to write, test, and debug the program.
• In C++, sub-tasks are implemented as functions.
– A function is a group of statements that is executed when it is
called from some point of the program.
– E.g., the main function main() in previous examples
• A program is composed of a collection of functions.
• When a program is put into execution, it always starts at
the main function, which may in turn call other functions.
6
Advantages of Using Functions
• May focus on a particular task, easy to construct and
debug
• Different people can work on different functions
simultaneously.
• A function is written once and can be reused multiple
times in a program or in different programs.
• Improve readability of a program by reducing the
complexity of main()
7
PREDEFINED FUNCTIONS VS.
SELF-DEFINED FUNCTIONS
8
Predefined Functions
• Some computations and operations are so common that
they are implemented as pre-defined functions that are
shared for use.
• Consider computing the square root of a number. It
would be nice if we have a black box function (i.e., we
don’t care how the computation is done) to help us do the
calculation.
Here, 5.29 is the function input and
e.g. double x = sqrt(5.29); the function output 2.3 would be
stored to x.
10
Examples
• Some commonly used predefined functions. It means that you
can make use of them in your program for a particular task
(e.g., computing the square root of a number) without the
need of writing that part of the code on your own.
The file containing all the
function declarations
• You can consult the C++ manuals, e.g. www.cplusplus.com, for the details of individual 11
functions.
Using Predefined Functions
• It is very important for a developer to be able to use
predefined functions, since there are many libraries out there
already well-written (and well-tested) by others for some
specific purposes, e.g., math libraries, image handling libraries,
linear algebra libraries.
• The function declarations in the header files should tell how
we may use each function in the given library, but usually we
will go to the library documentation (or manual/reference) to
look at the usage details for each function.
• It is therefore also very important for a developer to be able to
read and understand library documentation.
12
Function Reference Example
This is what you can find from the sqrt() function reference from cplusplus.com:
Note that a function may accept different data types as inputs (hence there are 4
different function declarations for sqrt()).
This is called function overloading.
In this case, if you provide a double type input to sqrt(), the output returned by the
function will be of a double type, if the input to sqrt() is of a float type, then the output
would be of a float type. 13
Using Predefined Functions
• To use a pre-defined function, simply include the
corresponding header file using the include directive #include
<…> at the beginning of the file containing your code
– e.g., #include<iostream> for using cin, cout, endl
– e.g., #include<cstdlib> for using rand(), srand()
• This step is mandatory so that the compiler can check if the
functions are used correctly. Recall that the header contains
how the functions should be used.
int main() {
// Compute the root mean square of 10 input numbers
int i;
double n, sq_sum = 0;
A function may accept one or more
for(i=0; i<10; i++) input parameters. The order and
{ type of each parameter matter.
cout << i+1 << ": "; Check the pow() reference page to
cin >> n;
sq_sum += pow(n, 2.0);
see what each parameter mean.
}
cout << "The root mean square is " << sqrt(sq_sum/10) << endl;
return 0;
}
15
Example: Random Number Generation
We need random numbers under many circumstances, e.g., games in which we
need to generate random scenarios for the players.
#include <iostream>
using namespace std;
Recall the simple number guessing game in Module 3.
int main()
{
int num = 23;
int guess;
We can see that the answer is always
bool isGuessed; 23 no matter how many times you
isGuessed = false; run the program, as this “answer” is
while (!isGuessed) {
hard-coded into the program. This is
cout << "Make a guess (0-99)? ";
cin >> guess;
not so interesting as a game.
if (guess == num) {
cout << "Correct!" << endl;
isGuessed = true; Let’s try add in some randomness for
} this game by making your program
else if (guess < num)
cout << "Too small. Guess again!" << endl; generate a different number
else
cout << "Too large. Guess again!" << endl;
randomly every time it is run.
}
return 0;
16
}
Example: Random Number Generation
We need the rand() function in <cstdlib> to generate a random number.
http://www.cplusplus.com/reference/cstdlib/rand/
Look at what it returns. The function returns a random integer in the range [0,
RAND_MAX], and RAND_MAX is a constant defined in <cstdlib>.
So how can we make use of it for generating a random integer in the range of [0, 9]?
Hint: use the modulus operator %, which computes the remainder of a division.
17
Example: Random Number Generation
Q.1 how to generate a random integer in the range of [0, 9]?
Answer: rand() % 10
18
Example: Random Number Generation
Take look at this program. What does it do?
#include <iostream>
#include <cstdlib> // needed for calling rand()
using namespace std;
int main()
{
for (int i = 0; i < 10; ++i)
cout << rand() % 100 + 1 << endl;
return 0;
}
Now, try to run the program. Write down the numbers that it generated.
Run the program again. And again. And again. What do you notice?
19
Example: Random Number Generation
The program generates the same 10 numbers for every run. So it’s not that “random”
after all. Why is that?
The algorithm for generating the sequence of random numbers depends on an initial
”seed” number. Given the same seed, the same sequence of numbers will be generated.
They are “random” in the sense that the distribution of the numbers in the sequence is
random.
This is why when you run your program, it always generates the same sequence because
we didn’t change the seed.
The seed for the random number generator can be specified by srand() function.
The function return type “void” means the function does not return any value.
20
Example: Random Number Generation
So our program should look something like this:
#include <iostream>
#include <cstdlib> // for calling srand(), rand()
using namespace std;
int main()
{
srand(???); // initialize the seed for rand()
for (int i = 0; i < 10; ++i)
cout << rand() % 100 + 1 << endl;
return 0;
}
Now what should we fill in for ??? ? The srand() takes a number which will be used as the
seed for generating number. But we need a different number every time we run the
program.
How about taking the current system time as the seed? In this way, we can guarantee a
different runtime value for every program run.
21
Example: Random Number Generation
We need another predefined function to obtain the current system time:
http://www.cplusplus.com/reference/ctime/time/
Go to the function reference page and read the details about the parameters and return
value, on how to get the current time using this time() function. The function call
time(NULL) will return the current time.
Or, look at the example on the reference page for rand() (http://www.cplusplus.com/reference/cstdlib/rand/)
and you can see that we can simply initialize the seed for the random number generator
with the current time by
// initialize random seed
srand(time(NULL));
22
Example: Random Number Generation
Now plug all these into our program for generating 10 random numbers:
#include <iostream>
#include <cstdlib> // for calling srand(), rand()
#include <ctime> // for calling time()
using namespace std;
int main()
{
srand(time(NULL)); // initialize the seed for rand()
for (int i = 0; i < 10; ++i)
cout << rand() % 100 + 1 << endl;
return 0;
}
Run the program again. And again. And again. We are done!
23
Example: Random Number Generation
Note: It turns out to be very important that we can specify the seed value. Sometimes we
do want to have the same sequence of random numbers to be generated for every run of
our program, especially for debugging.
Suppose you have a set of random numbers as input to part of your code, then imagine
how difficult it would be to debug your program if in every program run, these numbers
are different because the behavior of your program would be different. In this case, you
may want to fix the random seed, by supply the same number to srand(), such as
srand(0).
Now, a quick exercise. Can you modify the guessing game on P.19 so that it will generate a
random number (say, from 1 to 50) for the player?
Next, you will start writing your own functions in your program.
24
Defining Your Own Functions
Suppose you want to have a function which tells which of the two
given floating point numbers is larger.
These are the questions that you should ask (& answer):
Q1. What are the input(s) to the functions? What are their data type?
Two floating-point numbers, data type: double
Q2. What is the output of the function? What is its data type?
One floating-point number, data type: double
26
Defining Your Own Functions
To answer Q3, we need the actual computations inside the
function body:
double larger(double x, double y)
{
double max;
if (x >= y)
max = x;
function body else function parameters x and y
embraced by {} max = y; are used in the calculation
return max;
return statement
} • returns the specified value to the
max is the return value, and its caller
data type must agree with that • terminates the execution of the
specified in the function header function
(i.e., double)
27
FUNCTION DEFINITION, FUNCTION
CALL &
FUNCTION DECLARATION
28
Function Definition
Formally speaking, a function is defined using a function
definition which
• Describes how a function computes the value it returns
• Consists of a function header followed by a function body
list of parameters
type of return value function name (types and names)
Syntax
function type_ret func_name(type1 par1, type2 par2, …)
header
{
// variable declarations
function body …
embraced by {}
// executable statements
…
} 29
Void Functions
• In some situations, a function simply carries out some
operations and produces no return value.
• A function with no return value is called a void function.
• In this case, the void type specifier, which indicates
absence of type, can be used.
• The return statement in a void function does not specify
any return value. It is used to return the control to the
calling function.
• If a return statement is missing in a void function, the
control will be returned to the calling function after the
execution of the last statement in the function.
30
Void Functions
Examples
void print_msg(int x)
{
cout << "This is a void function " << x << endl;
return;
} A return statement
with no return value
void print_msg(int x)
{
cout << "This is a void function " << x << endl;
}
No return statement
arguments
33
Function Call
• The arguments used in a function call can be constants,
variables, expressions, or even function calls, e.g.,
Syntax
type_ret func_name(type1 par1, type2 par2, …);
or
type_ret func_name(type1, type2, …);
36
Function Declaration
Examples:
38
Flow of Control
• When a program is put into execution
– It always starts at the main function no matter where its definition is in the
source file.
– The statements in the main function are executed sequentially from top to
bottom and the control is passed from one statement to another.
– When a function call is encountered, the execution of the current function
is suspended.
• The values of the arguments are copied to the formal parameters of the called
function, and the control is passed to the called function.
• Likewise, the statements in the called function are executed from top to bottom,
and the control is passed from one statement to another.
• When a return statement is encountered, the execution of the function terminates.
• The control is passed back to the calling function together with the return value.
– The main function will resume at the calling statement.
– When a return statement in the main function is encountered, the
program ends.
39
Flow of Control
#include <iostream>
using namespace std;
The program on the right
consists of two functions: double larger(double x, double y)
{
• main(): controls general if (x >= y)
return x;
logic flow and handles I/O else
• larger(): determines the return y;
}
larger of two numbers
int main()
{
double a = 2.5, b = 5.0, c;
c = larger(a, b);
cout << c << " is larger." << endl;
return 0;
}
40
Flow of Control
• When a program is put into #include <iostream>
using namespace std;
execution, it always starts at
the main function no matter double larger(double x, double y)
{
where its definition is in the if (x >= y)
source file. return x;
else
return y;
}
int main()
{
double a = 2.5, b = 5.0, c;
c = larger(a, b);
cout << c << " is larger." << endl;
return 0;
}
41
Flow of Control
• The statements in the #include <iostream>
using namespace std;
main function are double larger(double x, double y)
executed sequentially {
if (x >= y)
from top to bottom. return x;
else
• The control is passed }
return y;
c = larger(a, b);
cout << c << " is larger." << endl;
return 0;
}
42
Flow of Control
• When a function call is #include <iostream>
using namespace std;
encountered, the
double larger(double x, double y)
execution of the current {
if (x >= y)
function is suspended. return x;
else
return y;
}
int main()
{
double a = 2.5, b = 5.0, c;
c = larger(a, b);
cout << c << " is larger." << endl;
return 0;
}
43
Flow of Control
• The values of the arguments #include <iostream>
using namespace std;
are copied to the formal
parameters of the called double larger(double x, double y)
function. {
if (x >= y)
• The control is passed back to return x; 5.0
else 2.5
the called function. return y;
}
int main()
{
double a = 2.5, b = 5.0, c;
c = larger(a, b);
cout << c << " is larger." << endl;
return 0;
}
44
Flow of Control
• Likewise, the statements #include <iostream>
using namespace std;
in the called function are
double larger(double x, double y)
executed from top to { 2.5 5.0
if (x >= y)
bottom return x;
• The control is passed else
return y;
from one statement to }
c = larger(a, b);
cout << c << " is larger." << endl;
return 0;
}
45
Flow of Control
• When a return statement #include <iostream>
using namespace std;
is encountered, the
double larger(double x, double y)
execution of the function { 2.5 5.0
if (x >= y)
terminates return x;
else
return y;
}
int main()
{
double a = 2.5, b = 5.0, c;
c = larger(a, b);
cout << c << " is larger." << endl;
return 0;
}
46
Flow of Control
• The control is passed #include <iostream>
using namespace std;
back to the calling
double larger(double x, double y)
function together with {
if (x >= y)
the return value return x;
else
return y;
}
int main()5.0
{
double a = 2.5, b = 5.0, c;
c = larger(a, b);
cout << c << " is larger." << endl;
return 0;
}
47
Flow of Control
• The main function will #include <iostream>
using namespace std;
resume at the calling double larger(double x, double y)
statement {
if (x >= y)
return x;
else
return y;
}
int main()
{
double a = 2.5, b = 5.0, c;
c = larger(a, b);
cout << c << " is larger." << endl;
return 0;
}
48
Flow of Control
• The statements in the #include <iostream>
using namespace std;
main function are double larger(double x, double y)
executed sequentially {
if (x >= y)
from top to bottom. return x;
else
return y;
• The control is passed }
c = larger(a, b);
cout << c << " is larger." << endl;
return 0;
}
c = larger(a, b);
cout << c << " is larger." << endl;
return 0;
}
50
Flow of Control
• When a return #include <iostream>
using namespace std;
statement in the main double larger(double x, double y)
function is {
if (x >= y)
encountered, the return x;
else
program ends. }
return y;
return 0;
}
52
Local Variables
• Variables declared within a function, including formal
parameters, are private or local to that particular
function, i.e., no other function can have direct access to
them.
• Local variables in a function come into existence only
when the function is called, and disappear when the
function is exited.
– Do not retain their values from one function call to another
– Their values must be explicitly set upon each entry
• Local variables declared within the same function must
have unique identifiers, whereas local variables of
different functions may use the same identifier.
53
Local Variables local variables of larger():
x, y, max
#include <iostream> i.e., these variables are
using namespace std; input parameters or
variables defined in the
double larger(double x, double y) function larger(), and
{ therefore can only be
double max; seen or used in larger()
max = (x >= y)? x : y; local variables of main():
a, b, max
return max; i.e., these variables are
} defined in the function
main(), and therefore can
int main() only be seen or used in
{ main()
double a = 2.5, b = 5.0, max;
int main()
{
double a = 2.5, b = 5.0, max;
return 0;
}
55
Global Variables
• Variables may also be declared outside all functions.
• Such variables are called global variables because they can
be accessed by all functions, i.e., globally accessible
within the file containing the program.
• Global variables remain in existence permanently.
– Retain their values even after the functions that set their values
have returned and exited
– Can be used instead of arguments to communicate data
between functions, however:
• The values of global variables can be changed by any functions.
• Hard to trace, especially when something goes wrong
• Not recommended and should be avoided!
• Frequently used as declared constant (whose values
cannot be changed)
56
Global Variables
#include <iostream>
using namespace std;
global variables:
double a, b;
a, b, PI
const double PI = 3.1415;
double larger()
{ The global constant PI can be
return (a >= b)? a : b; used throughout the file after its
} declaration.
int main()
{ Avoid using global variables
cout << "Input two integers: "; to communicate data between
cin >> a >> b; functions
cout << larger() << " is larger." << endl;
The variables a, b should best
double r; be changed into input
cout << "Input radius of a circle: "; parameters for the function
cin >> r; larger(). Can you do that?
cout << "Area of circle = " << PI * r * r << endl;
return 0;
} 57
Scopes of Variables
• The scope of a variable is the portion of a program
that the variable is well-defined and can be used.
• A variable cannot be accessed beyond its scope.
• The scope of a local / global variable starts from its
declaration up to the end of the block / file.
– A block is delimited by a pair of braces { }.
– Variables declared in outer blocks can be referred to in an
inner block.
• Variables can be declared with the same identifier as
long as they have different scopes.
– Variables in an inner block will hide any identically named
variables in outer blocks.
58
double a;
int func(int x, int y) Scopes of Variables
{
Scope of global variable a:
…
from declaration to end of block
if (x > y)
(in this case, end of file; hence
{
scope of a is the entire file)
int k;
…
}
Scope of formal parameters x, y:
int z;
entire function
…
}
double b;
int main() Scope of local variable k:
{ from declaration to end of block
int x, y, z; (in this case, end of if statement)
…
if (…)
{
int x; Scope of local variable z:
… from declaration to end of block
} (in this case, end of func)
…
}
59
double a;
int func(int x, int y) Scopes of Variables
{
… Scope of global variable b:
if (x > y) from declaration to end of block
{ (in this case, end of file)
int k;
…
Scope of local variables x, y, z:
}
from declaration to end of block
int z;
(in this case, end of main
…
function)
}
double b;
int main()
{
int x, y, z;
… Scope of local variable x in the
if (…) inner block:
{ from declaration to end of block
int x; (in this case, end of if
… statement)
}
… the outer x is hidden
} within this block
60
Scopes of Variables Screen output
Outer block: i = 0
#include <iostream> Inner block: i = 100
using namespace std;
Outer block: i = 0
int main()
{
int i = 0;
1 cout << "Outer block: i = " << i << endl;
{
int i = 100;
2 cout << "Inner block: i = " << i << endl;
}
61
PARAMETER PASSING
MECHANISM
62
Pass-by-Value
• When a function call takes place, the values of the
arguments are copied to the formal parameters of the
function.
• This mechanism of parameter-passing is known as
pass-by-value.
• Recall that formal parameters are local variables.
– Any changes made to their values are local to the function
and will not alter the arguments in the calling function.
– These variables will disappear when the function exits, only
the return value of the function will be passed back to the
calling function.
63
Pass-by-Value
#include <iostream> x is a
using namespace std; parameter
and also a
// computes the square of an integer
void square( int x )
x local
{ variable of
x *= x;
10 the square
} function
int main()
{
int a = 10; Copying of
cout << a << " squared: "; a value of
square( a ); actual
cout << a << endl;
10 argument to
return 0; } formal
parameter
64
Pass-by-Value
#include <iostream>
using namespace std;
int main()
{
int a = 10;
cout << a << " squared: "; a
square( a );
cout << a << endl;
10
return 0; }
65
Pass-by-Value
#include <iostream>
using namespace std;
Variable x disappears
// computes the square of an integer (more precisely, the
void square( int x ) memory location it
{
occupies is released
x *= x;
}
back to the system)
upon function
int main() completion.
{
int a = 10;
cout << a << " squared: "; a
square( a );
cout << a << endl;
10
return 0; }
66
Pass-by-Value Suppose we want to swap the values of the
#include <iostream> variables x and y using the function swap(),
using namespace std; what will happen in this program?
void swap(int a, int b)
Screen output
{
2 cout << "a = " << a << ", b = " << b << endl;
x = 0, y = 100
int temp = a;
a = b; a = 0, b = 100
b = temp; a = 100, b = 0
3 cout << "a = " << a << ", b = " << b << endl;
x = 0, y = 100
}
68
Pass-by-Reference
• To indicate a formal parameter will be passed by
reference, an ampersand sign & is placed in front of
its identifier in the function header and function
declaration.
69
Pass-by-Reference
Note the & to indicate that the
#include <iostream> formal parameter x is pass-by-
using namespace std; reference.
// computes the square of an integer
void square( int &x ) x
{
x *= x; Formal parameter
} refers to the
same memory
int main() location as the
{ argument
int a = 10;
cout << a << " squared: "; a
square( a );
cout << a << endl; 10
return 0;
}
70
Pass-by-Reference
#include <iostream>
using namespace std;
int main()
{
int a = 10;
cout << a << " squared: "; a
square( a );
cout << a << endl; 10
100
return 0;
}
71
Pass-by-Reference
#include <iostream>
using namespace std;
int main()
{
int a = 10;
cout << a << " squared: "; a
square( a );
cout << a << endl; 10
100
return 0;
}
72
Pass-by-Reference
What happens if we use pass-by-reference for
#include <iostream>
the swap function?
using namespace std;
73
Pass-by-Reference vs.
Value-Returning Function
• Call by Reference: modify the values of the actual
parameters in the calling function
• Value-Returning Function: returning a value that can be
used by the calling function
Call by Value Call by Reference
74
Pass-by-Reference vs.
Value-Returning Function
x = 2 before squareByValue
Value returned by squareByValue: 4
int squareByValue( int );
void squareByReference( int & ); x = 2 after squareByValue
int main() {
int a=10, b=20, c=30;
figureMeOut(a, b, c);
cout << a << ' ' << b << ' ' << c << endl;
} 77
Answer to Quick Exercise 1
Screen output:
10 20 30
1 2 3
1 20 3
78
We are happy to help you!
79