PIC 10AFunctions
Ernest RyuUCLA Mathematics
Functions
In mathematics, you input a value into a function and get an output.
We will start out by taking this viewpoint for functions. However,functions do a little more than just this in programming.
2
Defining a function
Define a function with
return_type function_name(input1_type input1_name,
input2_type input2_name, ...) {
code to execute
return return_value;
}
Once defined, call the function with
function_name(input1, input2, ...)
Let’s see this through an example.
3
Example: max
#include <iostream >
using namespace std;
double max(double a, double b) {
if (a>=b)
return a;
else
return b;
}
int main() {
cout << "Input 2 numbers" << endl;
double c; cin >> c;
double d; cin >> d;
cout << "The max is " << max(c,d) << endl;
}
4
Example: max
double max(double a, double b)
is called the function signature or function prototype. This functionsignature says
I the function is named max,
I max takes 2 inputs, named a and b,
I the 2 inputs are both of type double,
I and max returns a variable of type double.
We say
max(c,d)
returns or evaluates to a variable of type double.
5
Defining a function
A function definition is of the form
function_signature {
function body
}
Function signature:
I It defines the return type, function name, and list of inputs,
I It specifies the return type. Not returning anything is possible. Moreon this later.
I The list of inputs (also called parameters or arguments) each haveits own type and name. You can have no inputs. More on this later.
C++ requires the return type and and input types be specified atcompile-time.
6
Defining a function
A function definition is of the form
function_signature {
function body
}
Function body:
I It contains code to run when the function is called.
I It has access to the input arguments.
I It ends with return. (There are exceptions, which we’ll discuss.)
I return doesn’t have to be the last line of the function body, butthe flow of the function must eventually reach it.
7
Example: quadratic formula
The solutions/roots to the quadratic equation
ax2 + bx+ c = 0
is given by
x =−b±
√b2 − 4ac
2a.
Call D = b2 − 4ac the discriminant.
Let’s write functions for the quadratic formula with the followingcomplications in mind.
I If D < 0 there are 0 solutions. If D = 0 there is 1 solution. If D > 0there are 2 solutions.
I A function can’t return 2 values.
I When a = 0 you have division by 0 (an edge case).
8
Example: quadratic formula
double quad_form(double a, double b, double c) {
return (-b ?? sqrt(b*b-4*a*c))/(2*a);
}
Problem: we can’t return both roots of the quadratic equation.
9
Example: quadratic formula
double quad_form1(double a, double b, double c){
return (-b - sqrt(b*b-4*a*c))/(2*a);
}
double quad_form2(double a, double b, double c){
return (-b + sqrt(b*b-4*a*c))/(2*a);
}
Problem: we never defined the function sqrt. The compiler doesn’tknow what sqrt means.
10
Example: quadratic formula
#include <cmath >
using namespace std;
double quad_form1(double a, double b, double c){
return (-b - sqrt(b*b-4*a*c))/(2*a);
}
double quad_form2(double a, double b, double c){
return (-b + sqrt(b*b-4*a*c))/(2*a);
}
Problem: sqrt fails if its input is negative.
Inconvenient because we can only return one value from a function.
11
Functions as black boxes
In engineering and science, a black box is something you view in terms ofits inputs and outputs without any consideration of its internal workings.Consider functions (even the ones you write) black boxes.
I This viewpoint allows separation of concerns, which essentiallymeans you handle one issue at a time. This is absolutely crucialwhen writing complex programs.
I With separation of concerns, you first make the black box work (orget someone else’s black box) and then use it to do something else.
I When using a black box, its internal working is none of your business.
I You (probably) have no idea how sqrt works, and that’s fine. (It’ssomewhat complicated.)
12
The main function
The main function, which has signature
int main()
is the program’s entry point, i.e., the OS calls the main function whenyou run the program.
I A program must have one main function.
I The main function takes 0 inputs. (The main function can takeinputs but we won’t talk about this.)
I main returns an int, which tells the OS whether main was“successful”.
– return 0; means “main executed sucessfully”.– return X; with X not 0 means “something went wrong with main,
and the return value is the error code”.
I As an exception, the main function doesn’t have to return. Notreturning is the same as having return 0; at the end.
13
The main function
So we could write the hello world program as
#include <iostream >
using namespace std;
int main() {
cout << "Hello world" << endl;
return 0; //not mandatory
}
The OS won’t do anything with the return value by default. We won’tdiscuss main’s return value any more in this class.
14
The main function
return can still be useful in the main function, even through we don’tcare about its value.
#include <iostream >
using namespace std;
int main() {
while (true) {
cout << "What is the magic word?\n";
string s; cin >> s;
if (s == "please")
return 0;
cout << "That’s not the magic word.\n";
}
}
15
Function declaration
Declaring and defining a variable are the same. (The two words aresynonyms.) Declaring and defining a function are not the same. (Thetwo words are not synonyms.)
Declare a function with
function_signature;
This is also called forward declaration. You can declare a function, use it,and define it later.
Let’s see this through an example.
16
Function declaration
You can’t use a function before it’s declared or defined.
int main() {
cout << "Input 2 numbers" << endl;
double c; cin >> c;
double d; cin >> d;
cout << "The max is "
<< max(c,d) << endl; // Error!
return 0;
}
// function definition
double max(double a, double b) {
...
}
17
Function declaration
Once declared, the function can be called before its defined.
// function declaration
double max(double a, double b);
int main() {
cout << "Input 2 numbers" << endl;
double c; cin >> c;
double d; cin >> d;
cout << "The max is "
<< max(c,d) << endl;
return 0;
}
// function definition
double max(double a, double b) {
...
}
18
Function declaration
Of course, you can define a function without declaring it and use it.
// function definition
double max(double a, double b) {
...
}
int main() {
cout << "Input 2 numbers" << endl;
double c; cin >> c;
double d; cin >> d;
cout << "The max is "
<< max(c,d) << endl;
return 0;
}
19
Function declaration
Again, you can’t use a function before it’s declared or defined.
int main() {
cout << "Input 2 numbers" << endl;
double c; cin >> c;
double d; cin >> d;
cout << "The max is "
<< max(c,d) << endl; // Error!
return 0;
}
// function declaration
double max(double a, double b);
// function definition
double max(double a, double b) {
...
}
20
Function declaration and definition
I A function must be declared (or defined) before being used.#include is often used for this.
I A function definition can be provided anywhere: before or after itsusage or in another file.
I A function definition can be provided in another file without being#included.
I The difference stems from the fact that function definitions anddeclarations are used at different stages of the compilation. Wewon’t talk about this.
Let’s see this through an example.
21
Example: function declaration and definition
You can decare a function, use it, and then define it.
22
Example: function declaration and definition
You can decare a function, use it, and then define it.
23
Example: function declaration and definition
You can decare a function, use it, and then define it.
24
Example: function declaration and definition
We can use #include to put the declaration in another file.
25
Example: function declaration and definition
We can use #include to put the declaration in another file.
26
Example: function declaration and definition
We can use #include to put the declaration in another file.
27
Example: function declaration and definition
We can use #include to put the declaration in another file.
28
Example: function declaration and definition
We can use #include to put the declaration in another file.
29
Example: function declaration and definition
A function definition can be provided in another file without being#included.
30
Example: function declaration and definition
A function definition can be provided in another file without being#included.
31
Example: function declaration and definition
A function definition can be provided in another file without being#included.
32
Example: function declaration and definition
A function definition can be provided in another file without being#included.
33
Example: function declaration and definition
A function definition can be provided in another file without being#included.
34
Example: function declaration and definition
In VS 2015, the .cpp files within a project are compiled together.
35
Useful digression: managing multiple files
I The .h files are called header files. They contain declarations, byconvention.
I The .cpp files are called cpp files or source files. They containdefinitions, by convention.
I One of the .cpp files must have one main function. Usually,main.cpp is the dedicated file for the main function. (So there’s nomain.h.)
I You must #include declarations, which should be in .h files.
I You can but shouldn’t #include definitions, i.e., don’t#include "x.cpp". (We won’t discuss why.)
I Instead of #include-ing .cpp files, provide them to the compiler.VS 2015 automatically provides all .cpp files in a single project tothe compiler.
36
Useful digression: managing multiple files
I Collect functions of similar purpose and put their declarations insome_name.h and definitions in some_name.cpp.
I A function’s user (perhaps your coworker) reads the header file butnot necessarily the cpp file.
– Since declarations are much concise than definitions, the .h file ismore readible than the .cpp file.
– Comment in the .h file, not in the .cpp file, on how to use thefunction.
– This is in tune with the functions-as-blackboxes viewpoint.
I You’ll do this starting from hw6.
37
Scope
Variable scope, first mentioned in the discussion of flow control, isincredibly important when it comes to functions.
I Functions can access its arguments.
I Functions can’t access variables from the outside.
I Variables within functions are destroyed when the function returns.
I You can’t access variables inside a function from the outside(because they’re already gone).
I This is in tune with the functions-as-blackboxes viewpoint.
I These features are called information hiding. Information hidingclearly separates the internal matters of a function and the code thatcalls the function.
I Information hiding is a good thing, not a limitation.
38
Scope
int fn(int b) {
int c = b + 5;
int d = a; // error a not in scope
return c;
}
int main() {
int a = 0;
cout << fn(a) << endl; // output 5
cout << a << endl; // output 0
cout << b << endl; // error b not in scope
cout << c << endl; // error c not in scope
}
39
Pass by copy
In C++, function parameters are passed by copy or passed by value, i.e.,input parameters are copied and passed to the function.
int fn(int a) {
...
}
int main() {
int b = 2;
cout << fn(b) << endl;
}
2, the value of b, is copied into the local variable a and provided to fn.
40
Pass by copy
int fn(int a) {
a = a+2; // changes (a of fn)
cout << a << endl; // output 2
cout << &a << endl; // address of (a of fn)
return a;
}
int main() {
int a = 0;
cout << fn(a) << endl; // output 2
cout << a << endl; // output 0
cout << &a << endl; // address of (a of main)
}
The 2 memory addresses are different.
41
Global variables
Global variables have global scope, i.e., they can be accessed at any point.
#include <iostream >
using namespace std;
int g_count = 0; // global variable
int func(int a) {
g_count += a;
return g_count;
}
int main() {
cout << func(2) << endl; // output 2
cout << g_count << endl; // output 2
}
42
Global variables
Global variables can be useful but should be used sparingly.
I Global variables override information hiding and any notion of scope.
I In fact, cout and cin are global variables. These are indeed useful.
I People hate global variables for good reasons. (More on this whenwe discuss modular programming.)
I Beginners tend to overuse global variables. Whenever you want touse a global variable, think again.
You may not use global variables in this class.
43
Function with no inputs
If you view functions as things that merely takes an input and returnsoutput (the math viewpoint) functions with no inputs make no sense.Will the output be the same every time?
int return10 () {
return 10;
} //this function doesn’t seem useful
Functions with no inputs must access global variables (or objects itbelongs to) to be useful. We’ll talk about objects later.
44
Function with no inputs
#include <iostream >
#include <string >
#include <ctime >
using namespace std;
string get_time_string () {
// accessing a global variable
// provided by library ctime
time_t t = time(0);//No need to understand this
return asctime(localtime (&t));
}
int main() {
string s = get_time_string ();
cout << s;
}
Output is
Wed Nov 2 HH:MM:SS 2016
45
Function with no outputs
Functions that “return” type void, also called procedures, don’t returnanything.
You can still return procedures.
void skip3() {
cout << endl << endl << endl;
return;
cout << "we never get here\n";
}
Functions with return type void do not have to return.
46
Side effects
A function has a side effect if it somehow affects the outside world in away other than its return value.
In C++, a function can cause side effects by
I using global variables,
I modifying its arguments with pointers,
I modifying its arguments with references (which we’ll talk about),
I changing the object it belongs to (which we’ll talk about),
I raise an exception (which we won’t walk about),
I and calling another function that has side effects.
A function with return type void only make sense if it has side effects.
47
Modifying arguments with pointers
Even though variables are passed by copy, you can modify the inputsusing their pointers.
void swap(int* a, int* b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int a = 0, b = 1;
cout << a << ", " << b << endl; // output 0,1
swap(&a, &b);
cout << a << ", " << b << endl; // output 1,0
}
48
Using inputs as outputs
A function can return only one value, but it can output through inputs.
void quad_form(double a, double b, double c,
int* num_roots , double* root1 , double* root2);
int main() {
double a = 1.0, b = 2.1, c = 3.3;
int num_roots;
double root1 , root2;
quad_form(a, b, c, &num_roots , &root1 , &root2 );
if (num_roots == 0)
cout << "No roots" << endl;
if (num_roots == 1)
cout << "The one root is " << root1 << endl;
...
}
49
References
A reference is an alias to another variable.
int main() {
int a = 0;
int& b = a; //b refers to a
b = 1;
cout << a << endl; // output 1
cout << b << endl; // output 1
}
Unlike normal variables or pointers, references must be initialized as theyare defined.
50
References vs. pointers
Since we already have pointers, references may seem unnecessary.
int main() {
int a = 0;
int& b = a; //b refers to a
int* c = &a; //c points to a
b = 1;
cout << a << endl; // output 1
*c = 2;
cout << a << endl; // output 2
}
I Pointers can do everything references can do, but not vice versa.
I You can make a pointer point to something else, but you can’t makea reference refer to something else.
I References are used almost exlusively for function inputs and outputsand not as shown above.
51
Pass by reference
You can use references to have function parameters be passed byreference.
void swap(int& a, int& b) {
int temp = a;
a = b; //no dereferencing
b = temp;
}
int main() {
int a = 0, b = 1;
cout << a << ", " << b << endl;
swap(a, b); //no address -of operator
cout << a << ", " << b << endl;
}
Again, pointers can do everything references can do. Use whichever ismore convenient.
52
Pass by value vs. reference
This is an important distinction.
I In C++, default behavior is pass by value, but you can pass byreference if you use pointers or references.
I In languages like Fortran or Haskell, the default behavior is pass byreference, but there are ways to pass by value.
I In languages like Java or Python, the default behavior is a mixtureof pass by value and pass by reference. This can be confusing.
I In C and C++, you can emulate pass by reference by using pointers,but the syntax is less convenient.
53
Implicit type conversion
An input can be implicitly converted to match the function’s input type.
void print_int(int a) {
cout << "The int has value " << a << endl;
}
int main() {
double a = 3.6;
print_int(a); // implicit cast double ->int
}
This feature can be convenient, but it sometimes causes elusive bugs.
54
Function overloading
We say a function is overloaded if there are multiple functions of thesame name with different inputs.
void display(int var) {
cout << "Integer number: " << var << endl;
}
void display(float var) {
cout << "Float number: " << var << endl;
}
void display(int var1 , float var2) {
cout << "Integer number: " << var1;
cout << " and float number:" << var2;
}
The compiler determines which one to call based on the input’s type.
55
Operators are functions
So far, we have discussed the following operators: +, -, *, /, %, =, +=,-=, *=, /=, %=, ==, !=, <, >, <=, >=, !, &&, ||, ++, --, new, new[],delete, delete[], and &.
Really, operators are functions, and they are overloaded functions.
56
Operator +
You should think of + as functions that perform
int plus(int a, int b) {
int c;
//add a and b and assign to c
...
return c;
}
string plus(string a, string b) {
string c;
// concatenate a and b and assign to c
...
return c;
}
57
Operators ++
You should think of ++ as functions that perform
// Increment input and return the result
int pre_increment(int& a) {
a = a + 1;
return a;
} //(This is close to but not 100% true.)
// Increment input but return the original
int post_increment(int& a) {
int temp = a;
a = a + 1;
return temp;
}
58
Operators ++
Because of this difference, we get
int main() {
int i;
i = 0;
cout << ++i << endl; // output 1
cout << i << endl; // output 1
i = 0;
cout << i++ << endl; // output 0
cout << i << endl; // output 1
}
The operators -- work similarly.
You shouldn’t write code that relies on subtle rules.
59
Operator =
You should think of = as a function that performs
int assignment(int& LHS , int RHS) {
LHS = RHS;
return LHS;
}
when used with ints. So you could (but probably shouldn’t) write
int main() {
int i, j;
i = (j = 5); //I think this is bad style
cout << i << endl; // output 5
cout << j << endl; // output 5
}
60
Operator =
This explains the following bug.
int main() {
int i = 0;
if (i = 0) //we meant ==
cout << "hello" << endl; //won’t execute
}
i = 0 will return 0, and 0 will be cast into a bool of value false.
61
(Ab)using subtle rules
You can use fine and subtle rules to write terse code.
int main() {
char source[20] = "Hello";
char dest[20];
char *s = source , *d = dest;
while (*d++ = *s++) ; //bad style in my opinion
cout << dest << endl; // output Hello
}
Experienced programmers find terse code more readable, while beginnersprefer descriptive code. In my opinion, concise is good but terse is bad.
Terse code confuses those who don’t know the precise rules. That’s notgood.
62
Operator new
You should think of new as a function that performs
int* new_operator () {
// request memory from OS
...
return pointer_to_acquired_memory;
}
when used with type int.
63
Operator delete
You should think of delete as a function that performs
void delete_operator(int* p) {
//free memory from p to (p+sizeof(int)-1) to OS
...
return;
}
when used with type int.
64
Predicates
Functions that return a bool are called predicates. A predicate with 1input is called a unary predicate and a predicate with 2 inputs is called abinary predicate.
The operator ! is a unary predicate, and the operators ==, !=, <, >, <=,>=, &&, and || are binary predicates.
65
Revisiting the semicolon
As discussed in the flow control lectures, you terminate an expressionwith ; to get an expression statement.
This isn’t wrong, but it avoids the main point: the semicolon suppressesthe return value.
int i = 0; //not an expression statement
i++; // return value 0 suppressed
i = i + 9; // return value 10 suppressed
cout << (i=5); // return value is cout and it is
// suppressed (more on this later)
So when you wish to call a function but ignore its return value, use thesemicolon. (You still need ; even if the return value is void.)
66
Useful digression: debugger Step Into
The debugger functionality Step Into, bound to F11, is similar to StepOver, bound to F10, but it will enter function calls.
Inconveniently, you will also step into library functions, which youprobably have no interest debugging.
Sometimes, especially once we use objects, it may not be obvious whichfunction is being called. Step Into is very useful in debugging such code.
67
Functions (and objects) are everything
Organize programs around functions and objects.
I C++ is very object oriented, but some languages like C just havefunctions.
I Using function and objects (and avoiding global variables) facilitatemodular programming.
– Modular programming emphasizes separating the functionality of aprogram into independent, interchangeable modules.
– Modular programming achieves information hiding and separation ofconcerns.
I Draw the big picture with functions and objects, and then work onthe modules one at a time (separation of concerns). Hw7 illustratesthis.
68
Unit testing
An engineered product is often built out of many smaller parts, and thewhole can’t function properly unless the individual parts do. Unit testingtests the individual parts of a product.
I In C++, the units are function and objects.
I A way to demonstrate your units (probably) work as intended, youcan write test code or test harness.
I Often test code is written by someone other than the author of theunits.
I Well-written test code is reusable. You should keep it and rerun itwhen you modify your code.
I Test code gives your colleagues confidence in your code and yourproductivity ;)
I In a way, the main.cpp files we provide in the hws are test code.
69
Example
Demo quad_form.cpp.
70
Top Related