Schedule‎ > ‎

07B: More About Functions

Questions, Answers and Review

  • Questions from last class?
  • Questions about homework?

^ top

Overloading Functions and Reference Parameters

Learner Outcomes

At the end of the lesson the student will be able to:

  • Explain what is meant by overloading a function
  • Write multiple functions with the same name
  • Explain the difference between call-by-value and call-by reference parameter passing
  • Return values from functions using call-by-reference

^ top

Review of Functions

  • When we reuse a block of code in various parts of a program, we should put the code into a function
  • The biggest problem in developing software is managing the complexity of programs
  • Organizing code into functions redcuces repeated code and overall, the complexity of larger programs

Function Syntax

Parameters and Arguments

  • When we define a function, we want it to be reusable
  • To make a function more reusable, we avoid hard-coding important values
  • Instead, we pass the key values by defining parameters
  • When we call the function, we supply an argument for each parameter, like:

    Passing arguments in a function call

  • The argument gets copied to the function parameter when making the function call

Variable and Parameter Scope

  • Variables declared in one function are not available in any other function
  • Trying to use a local variable outside a function causes a compile-time error
  • Local variables are variables declared inside of any function
  • Global variables are variables declared outside of any function
  • Parameters are a type of local variable

Returning Values

  • If a function does not return a value, we use the return type: void
  • Otherwise, we specify the type of data we want to return
  • When functions return a value, the value replaces the function call like:

    Returning values from a function call

  • Notice that normally we can return only one value from a function

Check Yourself

  1. Which of the following are NOT a reason to write a function?
    1. Allow code reuse
    2. Reduce repeated code
    3. Force the use of a single variable
    4. Logically organize code
  2. Of the following, the valid function definition is ________.
    1. fun() { /* C/C++ statements */ }
    2. int fun;
    3. int fun() { /* C/C++ statements */ }
    4. int fun();
  3. To received input, a function has __________.
  4. If a function has three parameters, a function call must include ________ arguments.
    1. 0
    2. 1
    3. 2
    4. 3
  5. True or false: A variable that can be used only within the function in which it was declared is known as a local variable.
  6. To return a value from a function use a __________ statement.

^ top

Programming Style Requirements for Functions

  • Let us look more closely at the layout of the function shown above
  • Note the placement of the curly braces
  • There are two common styles of curly brace placement for functions:
    1. Place the opening brace on the same line as the function heading:
      void myFunction() {
          // statements of the function
      }
      
    2. Place the opening brace under and lined up with the first letter of the return type:
      void myFunction()
      {
          // statements of the function
      }
      
  • You can use either style as long as you are consistent
  • Also notice the indentation of the statements inside the function
  • As before, always indent after an opening curly brace
  • After the closing curly brace, you no longer indent the extra spaces
  • Indenting makes it easier to see the block of code
  • As we have seen before, the Auto Format (Ctrl + T) function does the indenting for us

Naming Functions

  • When making up a name for a function, use verbs since functions perform an action
  • There are two common naming styles you may use:
    1. Start with a lower-case letter and use uppercase letters as separators. Do not use underbars ('_'):
      int myFunction()
    2. Use all lower case letters and use underbars ('_') as separators:
      int my_function()

Commenting Functions

  • For your homework, every function besides setup() and loop() must have a Function Comment Block before the function
  • Comments are for human readers, not compilers
  • There is no universal standard for comment layout, but we use a style commonly used with many programming languages:
    1. Block comments start with /** and ends with */
    2. The first line explains the idea of the function, not the implementation
    3. An @param entry explains each parameter
    4. An @return entry describes the return value
  • The following example has a fully commented function

Example Program with Fully Commented Function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
  CS-11M Project 3A
  Name: my_multiply_doc
  Purpose: Multiplies two ints obtained from the user.

  @author Ed Parrish
  @version 1.0 8/04/15
*/
const int BAUD_RATE = 9600;

void setup() {
  Serial.begin(BAUD_RATE);
  Serial.println("Enter two numbers separated by a space");
}

void loop() {
  if (Serial.available()) {
    int a = Serial.parseFloat();
    int b = Serial.parseFloat();
    int c = myMultiplyFunction(a, b);
    String str = " * ";
    Serial.println(a + str + b + " = " + c);
  }
}

/**
  Multiply two numbers and return the result.
  
  @param x first quantity to multiply.
  @param y second quantity to multiply.
  @return result of the multiplication operation
*/
int myMultiplyFunction(int x, int y) {
  int result;
  result = x * y;
  return result;
}

Further Information

Check Yourself

  1. Of the following, ________ is an acceptable curly-brace placement and statement indentation style for function definitions.
    1. int fun() { /* C++ statements */ }
    2. int fun()
      { /* C++ statements */ }
    3. int fun() {
      /* C++ statements */ }
    4. int fun()
      {
           /* C++ statements */
      }
  2. True or false: functions names should start with lower-case letters.
  3. True or false: the first line of a function block comment explains the idea of the function, not the implementation.
  4. For every function parameter, you include a(n) ________ tag in the function block comment.
  5. If the function returns a value, you include a(n) ________ tag in the function block comment.

^ top

Overloading Functions

  • C++ lets us have more than one function with the same name
  • Having two or more function definitions with the same name is known as function overloading
  • Overloaded functions must have different parameter types between them
  • As an example, consider our previous example function
    int myMultiplyFunction(int x, int y) {
      int result;
      result = x * y;
      return result;
    }
    
  • The above function only uses integer multiplication
  • We could add a second function to handle floating-point multiplication, like:
    double myMultiplyFunction(double x, double y) {
      int result;
      result = x * y;
      return result;
    }
    
  • Now we will get different results when multiplying numbers with decimal points
    3.50 * 4.50 = 15.00
    3 * 4 = 12
    

Example Sketch with Overloaded Functions

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
void setup() {
Serial.begin(9600);
cout << "Enter two numbers separated by a space" << endl;
}
void loop() {
if (Serial.available()) {
double a = 0.0;
double b = 0.0;
cin >> a >> b;
double c = myMultiplyFunction(a,b);
cout << a << " * " << b << " = " << c << endl;
int ci = myMultiplyFunction((int)a, (int)b);
cout << (int) a << " * " << (int) b << " = " << ci << endl;
int ai = a; // convert double to int
int bi = b;
ci = myMultiplyFunction(ai, bi);
String str = " * ";
Serial.println(ai + str + bi + " = " + ci);
/* ci = myMultiplyFunction(ai,b); //mix float and int
cout << ai << " * " << b << " = " << ci << endl; */
}
}

int myMultiplyFunction(int x, int y) {
Serial.println("In integer version");
int result;
result = x * y;
return result;
}

double myMultiplyFunction(double x, double y) {
Serial.println("In double version");
int result;
result = x * y;
return result;
}

Overloading Resolution

  • Let us look at how function calls are matched with overloaded functions
  • The process the compiler follow is:
    1. Look for an exact signature match
    2. Look for a "compatible" signature
  • If an exact match is not found, C++ will try to convert the data type as follows:
    1. byte, char, unsigned char, and short are promoted to int
    2. unsigned short can be promoted to int if it can hold its entire value range or unsigned int otherwise
  • Some conversions may cause a loss of precision, so care must be taken
  • Also, C++ only knows how to convert certain types, mostly the numeric types
  • Thus, C++ does not automatically convert something like a String to a double

Type Conversion

  • C++ can convert between numerical types
  • We can manually specify a type conversion by using a typecast
  • To typecast, we place the desire type in parenthesis in front of the variable, like:
    double c = myMultiplyFunction((int) a, (int) b);
    
  • The above calls the myMultiplyFunction(int x, int y) function even though a and b are of type double
  • Another way to convert types is to use the conversion functions listed on the Arduino Language Reference page
  • Also, to convert a String to a numeric type, use one of the String object functions
  • In the above Function Overloading code,  uncomment the last 2 code lines in the loop() function to see what happens when you call myMultiplyFunction with an int and float parameters.

Avoiding Function Overloading

  • Usually we make numeric parameters type double
  • Type double will accept any numeric type
  • However, we will come across cases where we need to overload functions

Check Yourself

  1. Of the following function signatures, the two that are not allowed in the same scope are ________.
    1. int add(int a, int b)
    2. double add(int a, double b)
    3. double add(double a, double b
    4. double add(int x, int y)
  2. The reason the two function signature of the above are not allowed in the same scope is that ________.
    1. function names are the same
    2. parameter types are different
    3. parameter types are the same
    4. return types are different
  3. For the following function call, the function definition that is invoked is ________.
    fun(98, 99);
    
    1. void fun(int n, double m)
    2. void fun(double n, int m)
    3. void fun(int n, int m)
    4. void fun(double n, double m)

^ top

Value Parameters

  • There are two ways to pass arguments to parameters
  • All our functions so far have used value parameters
  • Value parameters are separate from the variables in the calling function
  • Modification of value parameters does not affect the original caller's value when the function returns

How Value Parameters Work

  • During the function call, your program copies the argument's value into the parameter variable
  • The scope of value parameters is the same as the scope for a local variable
  • If the parameter is assigned a new value, only the local copy changes
  • When the function returns, your program discards any value assigned to a parameter variable
  • The following example program uses value parameters
  • What does this program output if the user enters 42?

Example of Value Parameters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void setup() {
Serial.begin(9600);
cout << "Enter an integer" << endl;
}

void loop() {
if (Serial.available()) {
int num = 0;
cin >> num;
cout << "in loop: before calling mystery, num = " << num << endl;
int returnValue = mystery(num);
cout << "in loop: after calling mystery, returned value = " << returnValue << endl;
cout << "in loop: after calling mystery, num = " << num << endl;
}
}

int mystery(int param) {
cout << "inside mystery: at first, param = " << param << endl;
param = param * 2;
cout << "inside mystery: about to return, param = " << param << endl;
return param;
}

Demonstration: Calling a Function by Value

Setup
  • One student volunteer to act as a function, which performs a calculation and returns a value.
  • Two student volunteers to act as function callers.
  • All volunteers need a piece of paper, which represents a computer's memory.
Action
  1. Each function caller writes a number value on a piece of paper (the argument variable) and shows the paper to the function.
  2. The function:
    1. Looks at the paper from the caller and writes down the value.
    2. Performs the computation (doubling the value) and writes the new value on the paper
    3. Shows the answer to the caller.
  3. Each function caller then writes down the answer and shows the class the original value and the returned information.

Check Yourself

  1. True or false: the function gets a copy of the original information (argument).
  2. True or false: the function returns the new calculation.
  3. True or false: the original variable from the caller (written on the caller's paper) does not change.

^ top

Reference Parameters

  • C++ has another parameter-passing mechanism known as call-by-reference
  • A reference parameter does not create a new variable, but refers to the caller's existing variable instead
  • Any change to a reference parameter's value is actually a change in the variable to which it refers
  • We create a reference parameter by using an ampersand (&) between the parameter's type and name
    parameterType& parameterName
    
  • The following program shows an example of reference parameters
  • What is different?
  • What does this program output?

Example of Reference Parameters

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void setup() {
Serial.begin(9600);
cout << "Enter an integer";
}

void loop() {
if (Serial.available()) {
int num = 0;
cin >> num;
cout << "in loop: before calling mystery, num = " << num << endl;
int returnValue = mystery(num);
cout << "in loop: after calling mystery, returned value = " << returnValue << endl;
cout << "in loop: after calling mystery, num = " << num << endl;
}
}

int mystery(int& param) {
cout << "inside mystery: at first, param = " << param << endl;
param = param * 2;
cout << "inside mystery: about to return, param = " << param << endl;
return param;
}

Call-By-Reference Details

  • What's really passed to the reference parameter?
  • A reference to the caller's original argument!
  • Essentially a reference parameter is another name for the original argument
  • With a reference parameter the code uses the original argument variable in calculations
  • Any change made to a reference parameter changes the original argument
  • Because C++ is passing variables, arguments for reference parameters must be variables and not literal numbers or other constants

Demonstration: Calling a Function by Reference

Setup
  • One student volunteer to act as a function, which performs a calculation and returns a value.
  • Two student volunteers to act as function callers.
  • All volunteers need a piece of paper, which represents the computer's memory.
Action
  1. Each function caller writes a number value on a piece of paper (the argument variable) and hands the paper to the function.
  2. The function:
    1. Takes the paper (argument variable) from the caller
    2. Performs the computation (doubling the value) and writes the new value on the paper
    3. Gives the original paper (argument variable) back to the caller.
  3. Each function caller then shows the class the orginal paper (the argument variable) and the returned information with the new information.

Notice the similarities and differences to calling a function by value.

Check Yourself

  1. True or false: When an argument is passed to a reference parameter, the actual variable is sent to the function.
  2. True or false: when a variable is used as a call-by-reference argument, the value of the variable is never changed by the function call.
  3. True or false: you cannot pass a literal value to a call-by-reference parameter.
  4. What must a reference parameter refer to in a function call?
    1. A variable
    2. An expression
    3. A return statement
    4. A constant

^ top

Mixed Parameter Lists

  • Parameter lists can include both value and reference parameters
  • As usual, the order of arguments in the list is critical
  • The following is a function signature with mixed parameter types:
    void mixedCall(int& par1, int par2, double& par3)
    
  • To call the function:
    int arg1 = 0, arg2 = 1;
    double arg3 = 2.2;
    mixedCall(arg1, arg2, arg3);
    
  • arg1 must be an integer type and is passed by reference
  • arg2 must be an integer type and is passed by value
  • arg3 must be a double type and is passed by reference

Check Yourself

  1. True or false: you can include both call-by-value and call-by-reference parameters in the same function definition.
  2. True or false: the order of the parameters determines which arguments are passed by value or reference.
  3. In the following code snippet, the argument passed by reference is ________.
    fun(arg1, arg2, arg3);
    //...
    void fun(int par1, int par2&, double par3)
    
    1. arg1
    2. arg2
    3. arg3
    4. none of these
  4. In the previous question, we can tell which argument is call by reference because of the ________.

^ top

When to Use Reference Parameters

  • Reference parameters are usually more efficient than value parameters because they do not make copies of the parameters:
    • A program simply passes the memory address to the function
    • No new memory space is allocated and deallocated
  • Therefore, function calls using reference parameters usually operate faster
  • However, reference parameters restrict the arguments we can use for a function
  • Specifically, we must use a variable argument and not a literal or constant value
  • Usually, the best practice is to pass an object, like a String, by reference
  • Also, we should use value parameters unless a function needs to modify a parameter
  • The performance advantage of reference parameters for primitive types tends to be negligible
  • Thus, it is not worth restricting the call pattern of a function

Check Yourself

  1. True or false: it is always better to use call by reference because of the efficiency gains.
  2. Call-by-reference is more efficient than call-by-value because ________.
    1. less data is transferred for complex types
    2. memory is a list addresses, which is easier for the computer to process
    3. we can pass literal values to the function
    4. we can pass constants to the function
  3. True or false: call-by-reference places more restrictions on arguments to functions, such as prohibiting literal values as arguments.

^ top

Exercise 1: Exploring Call by Reference (3m)

In this exercise we explore how call-by-reference parameters differ from call-by-value parameters.

Specifications

  1. Start the Arduino IDE, copy the starter code below and paste it into the main IDE window.
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    
    void setup() {
    Serial.begin(9600);
    cout << "Enter two numbers separated by a space: " << endl;
    }

    void loop() {
    if (Serial.available()) {
    int a = 0;
    int b = 0;
    cin >> a >> b;
    cout << a << " " << b << endl;
    swap(a, b);
    cout << "After calling function swap: " << a << " " << b << endl;
    }
    }

    void swap(int var1, int var2) {
    int temp = var1;
    var1 = var2;
    var2 = temp;
    }
  2. Save the project using the name swap (File > Save As...) to a convenient location like the Desktop or the Arduino projects folder.
  3. Compile the sketch to verify you copied the starter code correctly.

    When compiling is successful, you will see a message at the bottom of the source code window saying, "Done compiling."

  4. Upload and run the sketch. When you run the program, the output should look like this:
    Enter two numbers separated by a space: 1 2
    After calling function: 1 2
    

    Notice that num1 and num2 have the same values before and after calling the function swap(). Any value assigned to var1 and var2 have no effect on num1 and num2..

  5. Change your program so that swap takes two call by reference parameters.  Do this by adding ampersands (&) to the swap function.
  6. The ampersands tell C++ to use call-by-reference when passing parameter values.

  7. Compile and run the modified program to make sure you made the changes correctly. When you run the program, the output should look like this:
    Enter two numbers separated by a space: 1 2
    After calling function: 2 1
    

    Notice that num1 and num2 have different values before and after calling the function swap(). Any value assigned to var1 and var2 change num1 and num2 respectively.

  8. Save your swap.ino file to submit to Canvas.

When finished, please help those around you.

^ top

Summary

Overloading
  • C++ lets us have more than one function with the same name
  • Having two or more function definitions with the same name is known as function overloading
  • Overloaded functions must have different parameter types between them
  • Since there is more than one function with the same name, the compiler searches for the correct match as follows:
    1. Look for an exact signature match
    2. Look for a "compatible" signature
  • If an exact match is not found, C++ will try to automatically convert the data type
  • However, C++ only knows how to convert certain types, mostly the numeric types
  • Thus, C++ does not automatically convert something like a String to a double
  • We can manually specify the conversions by using a typecast
  • To typecast, we place the desire type in parenthesis in front of the variable, like:
    double a = 1.23, b = 2.34;
    double c = myMultiplyFunction((int) a, (int) b);
    
  • The above calls the myMultiplyFunction(int x, int y) function even though a and b are of type double
Reference Parameters
  • There are two ways to pass arguments to parameters:
  • Value parameters are separate from the variables in the calling function
  • Modification of value parameters does not affect the original value
  • Call-by-references works differently than call-by-value
  • A reference parameter does not create a new variable, but refers to the caller's existing variable instead
  • Any change in a reference parameter is actually a change in the variable to which it refers
  • We create a reference parameter by using an ampersand '&' between the parameter's type and name
  • Arguments for reference parameters must be variables, not literal values or other constants
  • We can mix value and reference parameters in a function, like:
    void mixedCall(int& par1, int par2, double& par3);
    
  • We typically use reference parameters when:
    • We pass an object like a string to a function
    • We need to return more than one value

^ top

User Input Functions

Learner Outcomes

At the end of the lesson the student will be able to:

  • Write function to get user input.
  • Validate user input.

^ top

Using Functions to Read Input

  • One of the purposes of functions is to organize code into logical blocks or chunks
  • One useful chunk is getting user input
  • The following code shows an example of a user input function

Input Function

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
void setup() {
Serial.begin(9600);
}

void loop() {
double x = readDouble("Enter a number: ");
cout << "You entered: " << x << endl;
delay(1000);
}

// Blocking version
double readDouble(String prompt) {
Serial.print(prompt); //why do we have to use Serial.print here?
double input = 0.0;
cin >> input;
cout << input << endl;
return input;
}

Analyzing the Code

  • The code blocks and waits for user input
    cin >> input;
    
  • Once the user enters some data, the program continues and returns any numerical input received
  • Notice that we both prompt the user and echo the input
    Serial.print(prompt);  // prompt user
    // ... other code here
    cout << input << endl; // echo input
    
  • Both of these features are important for user-friendly programs

Check Yourself

  1. True or false: organizing user input code is a good reason to write a function.
  2. Of the following statements, the one that blocks (stops a program from continuing) is ________.
    1. Serial.print(prompt);
    2. while (!Serial.available());
    3. cin >> input;
    4. return input;
  3. True or false: user-friendly code makes programs easier for users to operate.

^ top

Validating User Input

  • One of the problems with the user-input function is that it does not validate the input
  • For instance, if we want to calculate the square root of a number, the number must be positive
  • Otherwise, we get a mysterious number like:
    sqrt(-2.00) = NAN
    
  • We can modify our input functions to ensure we get correct input by adding a loop as shown in the following example

Input Validation with do-while Loop

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
void setup() {
Serial.begin(9600);
cout << "Square Root Calculator" << endl;
}

void loop() {
double x = readPosNum("Enter a positive number: ");
cout << "sqrt(" << x << ") = " << sqrt(x) << endl;
}

double readPosNum(String prompt) {
double input = -1;
do {
Serial.print(prompt);
cin >> input;
while (Serial.available()) { //put this after your cin statement
Serial.read();
}
cout << input << endl;
if (input <= 0) {
cout << "Error: number must be > 0" << endl;
}
} while (input <= 0);
return input;
}
  • In the readPosNum() function we verify that the input is a positive number
  • If the input is not positive, we use a loop to make the user re-enter the number
    do {
    // ... input code here
    } while (x <= 0);
    
  • In addition, we use an if-statement to detect an error and print an error message
  • Only when the user enters a positive number, does the loop exit and the function return

Check Yourself

  1. True or false: in the readPosNum() function, the variable input must be initialized to a number less that or equal to zero to enter the loop.
  2. True or false: input validation, like the above code, contains an indefinite loop.
  3. The purpose of the do-while-statement in the readPosNum() function is to ________.
    1. initialize the input variable
    2. check for errors
    3. loop when an error is found
    4. print the value entered
  4. The purpose of an if-statement when validating input is to ________.
    1. initialize the input variable
    2. display a message on error
    3. loop when an error is found
    4. print the value entered

^ top

Exercise 2: Adding Input Functions (5m)

In this exercise we update swap from the last exercise to read the user input using the readDouble() function.

Specifications

  1. Start the Arduino IDE with the swap project from the last exercise.
  2. Add a user input check to make sure both numbers are positive by doing steps 3 to 8 below
  3. Use a do-while loop in a function similar to readPosNum above.
  4. in loop() Replace the cin call  with two calls to readPosNum
  5.     int a = readPosNum("Enter a positive number: ");
        int b = readPosNum("Enter a positive number: ");    
                                                                          
  6. in loop() remove the if (Serial.available()) {   as well as its matching closing bracket as it is not needed here anymore.
  7. You will need to change the return type of function readPosNum to integer.
  8. Delete the user prompt in setup();
  9. Compile and upload your sketch, and verify it works correctly.

    If you have problems, ask a classmate or the instructor for help.

  10. Submit your updated swapCheckInput.ino file to Canvas.

When finished, please help those around you.

^ top

Wrap Up and Reminders

  • For the next homework, see the schedule
  • When class is over, please shut down your computer.
  • Complete unfinished exercises from today before the next class meeting

^ top

Comments