Home‎ > ‎CS 11M‎ > ‎

Introduction to Functions

Questions, Answers and Review

  • Questions from last class?
  • Questions about labs or upcoming homework?

Coding Functions

Writing Functions

  • Some of the setup() and loop() functions in our programs have been getting lengthy and complicated
  • The biggest problem in developing software is managing the complexity of programs
  • We can improve our code by organizing it into smaller pieces known as functions
  • Functions are a key tool in creating easy-to-understand programs that can be changed without problems

What is a Function?

  • As developers, we need to know how to write and call functions

    Function: a named block of statements that can receive input, perform an action, and optionally return a value

  • Functions are like little programs in our larger program
  • We give each little function something we want it to do
  • We then call the function and cause it to be executed from anywhere within our program
  • When the function has finished running, program execution returns to the point just after the code that called the function

Check Yourself

  1. True or false: people write functions to organize code into small understandable pieces.
  2. Which of the following are features of a function?
    1. Must have a name
    2. Can receive input
    3. Performs an action
    4. Must return a value
  3. True or false: when a function finishes executing, the program flow returns to the point just after the code that called the function.

Defining a Function

  • In this section we look at the syntax of function definitions and examine a simple example function
  • After we understand the syntax we can write more complicated functions

Function Syntax

  • The general syntax for defining a function is:
    returnType functionName(parameter1, ..., parametern) {
        statements
    }
    
  • Where:
    • returnType: the data type of the value returned
    • functionName: the name you make up for the function
    • parameterx: the input values, if any
    • statements: the list of statements to execute when the function is called
  • Can you identify each of these syntax items in the functions we have always used?
    void setup() {
      // put your setup code here, to run once:
    }
    
    void loop() {
      // put your main code here, to run repeatedly:
    }

Example of Adding Another Function Definition

  • A good example of code that should be in a function is blinking an LED
  • We modify the blink program we looked at before to use a function named blinkLED()
    int ledPin = 13;
    int delayPeriod = 250;
    
    void setup() {
      pinMode(ledPin, OUTPUT);
    }
    
    void loop() {
      blinkLED(); // function call
      delay(3000);
    }
    
    // function definition
    void blinkLED() {
      digitalWrite(ledPin, HIGH);
      delay(delayPeriod);
      digitalWrite(ledPin, LOW);
      delay(delayPeriod);
    }
    
  • We have moved the four lines for blinking an LED into a separate function
  • Now we can make the LED blink anytime by coding:
    blinkLED();
    

Function Name

  • To be able to call a function, every function must have a unique name
  • Function names follow the same rules as variable names
  • Technically, we can use any valid identifier for a function name
  • However, we should use a name that suggests the action the function performs
  • In our example, blinkLED suggests that the function will turn an LED on and off

Function Structure

  • The first line of a function is known as the function signature
    void blinkLED()
    
  • The curly braces {...} contain the function body
    {
      digitalWrite(ledPin, HIGH);
      delay(delayPeriod);
      digitalWrite(ledPin, LOW);
      delay(delayPeriod);
    }
    
  • The function body is the list of statement the function executes when called with the command:
    blinkLED();
    
  • The function signature describes the name, inputs and output of a function
  • We will look at these features in more detail in the following sections

Check Yourself

  1. True or false: function names are case sensitive.
  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. The commands a function executes are surrounded by ________.
    1. Curly braces -- { }
    2. Parenthesis -- ( )
    3. Square brackets -- [ ]
    4. Colons -- : :
  4. Which of the following is a function definition and which is a function call?
    1. int drawSquare(int x, int y) { /* statements */ }
    2. drawSquare(4, 2);

Parameters

  • When defining a function, it is worth thinking about what helpful action it will perform
  • For our function blinkLED() the action is obvious -- blink an LED!
  • However, we can make the function more useful if we give it parameters
  • The parameters for blinking an LED might include:
    • How many times to flash the LED
    • How long to flash the LED
  • In the following code we added parameters to the blinkLED() function
  • Read through the following code to identify how the code makes use of the parameters

Example Code with Function Parameters

const int ledPin = 13;

void setup() {
  pinMode(ledPin, OUTPUT);
}

void loop() {
  blinkLED(10, 250);
  delay(3000);
}

void blinkLED(int numBlinks, int duration) {
  for (int i = 0; i < numBlinks; i++) {
    digitalWrite(ledPin, HIGH);
    delay(duration);
    digitalWrite(ledPin, LOW);
    delay(duration);
  }
}

Parameter List
  • We must have parenthesis after a function name
  • Inside the parenthesis, we define a list of zero or more parameters
  • Parameters are the inputs to a function
  • In our example, we have two parameters inside the parenthesis
    void blinkLED(int numBlinks, int duration)
    
  • Parameters are the declaration of a new variable, even though they are declared inside parenthesis
  • Each parameter must have both a type and a name, just like a regular variable
  • If we have more than one parameter, we separate them with commas
  • Any parameter that we declare must be given an argument when we call the function
  • In the following image, the arguments 10 and 250 are copied to the parameters numBlinks and duration

Passing Arguments to a Function

Passing arguments in a function call

Arguments and Parameters

  • Depending on our background, we might use the term arguments or parameters for the values passed to functions
  • The terminology is not that important
  • However, the way I will use the terms is:
    • A function definition has parameters to receive values
      // numBlinks and duration are parameters
      void blinkLED(int numBlinks, int duration) {
          // ...
      }
      
    • A function call passes arguments to parameters
      blinkLED(10, 250); // 10 and 250 are arguments
      
  • Arguments are values we pass into functions
  • When the argument drops into a function, it lands in a parameter
  • A parameter is just like other variables declared inside the function
    • Except that a parameter gets initialized by an argument
  • The important part is:

    We must pass every function parameter an argument.

  • The arguments must be in the same order as the parameters
  • Also, the argument value must be compatible with the type of the parameter
  • For example, we cannot call blinkLED() with: blinkLED("LED", "fast")

Check Yourself

  1. To received input, a function has __________.
  2. True or false: like any other variable, a function parameter has both a type and a name.
  3. If a function has three parameters, a function call must include ________ arguments.
  4. The following code snippet prints ________.
    void setup() {
      Serial.begin(9600);
      display(42);
    }
    void display(int x) {
      cout << x << endl;
    }
    //... (more code here)
    

Variable and Parameter Scope

  • A variable declared inside a function can only be used within that function

    Local variable: a variable that can only be accessed within a function or block.

  • Like any other local variable, parameters can only be used inside the function in which they are declared
  • For example, we declared both numBlinks and duration inside the blinkLED() function:
    void blinkLED(int numBlinks, int duration) {
        // ...
    }
    
  • We cannot access either of these parameters inside the loop() or setup() functions
  • So if we tried to assign numBlinks a value inside the loop() function we would get a compiler error
    void loop() {
      numBlinks = 42; // NO!
      //...
    }
    

Scope

  • The area of code that a variable can operate within is known as it's scope

    Scope: the enclosing area within which a programming entity, such as a variable, exists

  • Because of scope, we can use variables with the same name in different functions
  • Variables declared inside of functions are known as local variables
  • Variables declared outside of any function are known as global variables
  • Experienced programmers avoid using global variables whenever possible
  • The reason is that because global variables can be accessed anywhere, they make programs hard to debug and change

Example of Scope

#include <ArduinoSTL.h> using namespace std; int x = 7; void setup() { Serial.begin(9600); cout << x << endl; // prints 7 x = 11; int x = 42; // new variable also named x cout << x << endl; // prints 42 not 7 x = 24; } void loop() { cout << x << endl; // What will this print? int x = 3; cout << x << endl;; // What will this print? delay(5000); }

Check Yourself

  1. A variable that can be used only within the function in which it was declared is known as a __________ variable.
  2. True or false: parameters are local variables.
  3. The area of a program within which a variable exists is known as its ________.
  4. The following code snippet prints ________.
    int x = 12;
    void setup() {
      Serial.begin(9600);
      x = 2;
      x = x + 7;
    }
    void loop() {
      cout << x;
    }
    
    1. 7
    2. 9
    3. 12
    4. 19
  5. The following code snippet prints ________.
    int x = 12;
    void setup() {
      Serial.begin(9600);
      int x = 2;
      x = x + 7;
    }
    void loop() {
      cout << x << endl;
    }
    
    1. 2
    2. 9
    3. 12
    4. 19
  6. The following code snippet prints ________.
    int x = 12;
    void setup() {
      Serial.begin(9600);
      int x = 2;
      x = x + 7;
    }
    void loop() {
      display(x - 5);
    }
    void display(int x) {
        cout << x << endl;
    }
    
    1. 4
    2. 7
    3. 9
    4. 12

More Information

Returning Values

  • The first word in the function signature is the return type
    void setup() {
      //...
    }
    
  • So far we have used void return types, which means the function returns "nothing"
  • Let us define a new function with a non-void return type

Example of a Function with a Non-void Return Type

double fahrenToCelsius(double fahren) {
  double celsius = (fahren - 32) * 5 / 9;
  return celsius;
}

Returning a Value

  • The function definition now starts with double rather than void
  • The word double indicates the function returns a numerical value of type double to the code calling the function
  • Calling the function might look like this:
    double celsiusTemp = fahrenToCelsius(32);
    
  • Any non-void function must have at least one return statement
    return celsius;
    
  • A return statement causes execution to leave the current function
  • When returning, program execution resumes immediately at the point immediately after where the function was called
  • The return statement must return data of a compatible type
  • The returned value is substituted for the function call in the calling code
    celsius =>[replaces]=> fahrenToCelsius(fTemp)
    
  • We must save the returned value if we want to process it later in the program
    double celsiusTemp = fahrenToCelsius(fTemp);
    

Returning a Value from a Function

Returning values from a function call

Returning an Expression

  • The value after the word return can be an expression
  • It does not have to be just the name of a variable
  • We could rewrite our return statement to the following:
    return (fahren - 32) * 5 / 9;
    

Multiple return Statements

  • We can have more than one return statement in a function
  • The first return statement reached is the one executed
  • We might have multiple returns if we have if-statements with alternate actions, like:
    if (analogRead(A0) > 400) {
        return 1;
    } else {
        return 0;
    }
    

Returning in a void Function

  • We can code a return statement in a void function as well, like:
    return;
    
  • With a void return type, no value can follow the word return
  • If no return statement is specified, the function returns after executing the last statement of the function

Check Yourself

  1. To return a value from a function use a __________ statement.
  2. True or false: a value returned from a function must be compatible with the function return type.
  3. To correctly assign the result of a function named getValue() to a variable named volts, use __________.
    1. volts = getValue();
    2. volts = getValue;
    3. volts(getValue);
    4. volts() = getValue();
  4. The following code snippet prints ________.
    void setup() {
      Serial.begin(9600);
      int x = get42() + 2;
      cout << x << endl;
    }
    int get42() {
      return 42;
    }
    //... (more code here)
    

More Information

Exercise 1: Writing a Function

In this exercise we define our own function.

Parts

  • Arduino board
  • USB cable

Starter Code

void setup() { Serial.begin(9600); Serial.println("Enter two integers separated by a space"); } void loop() {  int a;  int b;  cin >> a >> b; int c = myMultiplyFunction(a, b);  cout << a << " * " << b << " = " << c << endl; }

Specifications

  1. Start the Arduino IDE, copy the starter code above and paste it into the main IDE window.
    Notice that the timeout is not infinite and there is an if-statement checking Serial.available(). This implies No line ending in the Serial monitor.
  2. Save the project using the name multiply (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. Write the signature for a function named myMultiplyFunction that has two int parameters and returns an int value:
    returnType myMultiplyFunction(two_int_parameters)
    
  5. In addition, add the curly braces for the function body: { }.

    At this point your new function should compile.

  6. Uncomment the two lines in the loop() function and press the verify button.

    Your code should compile. If you have problems ask a classmate or the instructor for help.

  7. Inside the function body, multiply the two parameters and return the product of the numbers.
  8. Compile and upload your code to verify it works correctly.

    You should now be able to see your sketch work and produce an output like the following.

    Enter two integers separated by a space
    7 * 6 = 42
    
    If you have problems, ask a classmate or the instructor for help.
  9. Verify your code with the example shown below. Your code does not need to be the same but comparing helps understand other ways of coding.
  10. Save your multiply.ino file to submit to Canvas with the next homework.

When finished, please help those around you.

Flow of Control for a Function Call

  • To use functions well, we must understand the flow of a program when calling functions
  • Every Arduino sketch starts executing in the setup() function
  • After completing setup(), Arduino starts running the loop() function
  • When a program gets to a statement like the following, it stops executing and jumps to the function:
    int c = myMultiplyFunction(a, b);
    
  • The program executes the statements in the function and then returns to the point in the code right after where it jumped
  • When the function returns, the returned value replaces the function call
  • After returning, the program completes processing the calling statement and then moves on to the next statement
  • In our example, the statement saves the returned value in the variable: c
  • Every time the flow of control reaches a function call, the program:
    1. Temporarily stops executing in the current function
    2. Jumps to the called function and executes the statements of that function
    3. Returns to the point in the code just after where it jumped

Function Call Flow

Flow of control for a function call
  1. Every sketch starts executing at the start of the function setup().
  2. Arduino starts running the loop() function
  3. When reaching a function call, arguments are copied to the parameters.
  4. Function code executes until reaching a return statement.
  5. Return statement returns a value to the function call.
  6. Calling function continues after the function returns.

Activity: Use the Function, Luke!

Actors

  • Parameters (2)
  • Calculator (1)
  • Return Statement (1)
  • Function Callers (1+)

Setup

  • There are two separate areas: function and caller.
  • The Parameters, Calculator and Return Statement stand in the function area.
  • The Function Callers stand in the caller area.

Actions

  1. Function Caller calls (walks over to) the function area, tells the Parameters their arguments and walks back to the caller area.
  2. Calculation Maker asks each Parameter their value and makes the computation, telling the result to Return Statement.
  3. Return Statement walks to the caller area, tells the result to the Function Caller, and walks back to the function area.
  4. Function Caller announces the result of the computation.

Check Yourself

  1. Every sketch starts executing at the start of the function named __________.
  2. True or false: when reaching a function call, the currently executing function stops and control jumps to the start of the called function.
  3. The function call sends information to the __________.
  4. The return statement sends information (arguments) to the __________.
  5. True or false: after a called function returns, control returns to the point in the code just after where it was called.

Summary

  • When we reuse a block of code in various parts of a program, we should put the code into a function
  • Here are the main parts of an example function applicable to both C and C++ Function anatomy

    Source: Arduino.cc Functions documentation

  • 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:
    int x = myMultiplyFunction(7, 6);
    
  • 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
  • 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

Constants and Magic Numbers

  • C++ allows variables to be defined as constant
  • A constant variable (or constant) is a variable that cannot change after being assigned a value
  • Sounds oxymoronic, but is actually quite useful
  • To declare a constant, we use the keyword: const
    const int MY_CONST = 1;
    
  • As a matter of style, global constants are usually written in all uppercase letters with an underscore separator
  • Note that we must assign a value when the constant is declared
  • Because global constants cannot change and have the same value everywhere, global constants are considered good programming practice
  • Global constants are often used to assign names to numbers

Magic Numbers

  • Notice the numbers in the sketch from the last activity
    void setup() {
      Serial.begin(9600);
    }
    
    void loop() {
      int reading = analogRead(A0);
      double voltage = (reading * 5.0) / 1024;
      cout << voltage << " volts" << endl;
      delay(2000);
    }
    
  • The numbers are important to the program, but what do they mean?
  • Numbers like these are called "magic numbers"
  • They are magic because the value or presence is unexplainable without more knowledge
  • Often, no one knows what they mean after 3 months, including the author
  • The numbers just work -- its like magic!

What the Numbers Mean

  • 9600: BAUD rate of the serial communications port
  • 5.0: supply voltage
  • 1024: 210, the number of steps for the analog-to-digital (A/D) converter
  • 2000: delay between each loop in milliseconds

Avoiding Magic Numbers

  • A software engineer can often infer the meaning of numbers after reading the code carefully
  • A much better coding style is to avoid the use of magic numbers
  • Instead, break out the numbers used in a formula to show the derivation
  • Thus reading * 0.48828125 - 50 becomes:
    double voltage = (reading * 5.0) / 1024;
    double tempC = (voltage - 0.5) * 100;
    
  • In addition, use named constants to document the numbers whenever possible
  • The following is a better way to write the program

Example Sketch without Magic Numbers

#include <ArduinoSTL.h> using namespace std; const float R2 = 10000; const float PowerSupplyVoltage = 5.0; const int ADCSteps = 1024; void setup() { Serial.begin(9600); } void loop() { int reading = analogRead(A1); float voltage = (PowerSupplyVoltage * reading) / ADCSteps; float resistance = ((PowerSupplyVoltage * R2) / voltage ) - R2; cout << "Pressure Sensor Resistance: " << resistance << endl; delay(1000); }

More Information

Static Variables

  • Sometimes we need to remember the value of a variable from one loop() to the next
  • This can lead us to use a global variable
  • Instead, we can use a static variable
  • A static variable inside a function keeps its value between function calls
  • Thus a static variable has more limited scope
  • Variables declared as static are only created and initialized the first time a function is called
  • We can use this static property of variables to avoid global variables

Example Case for Using static Variables

  • Recall our blink program with a variable delay period
  • We needed a global variable to remember the current delay period
    int delayPeriod = 100;
    
  • If we simply place the variable inside a function, the program no longer functions correctly
  • Every time the function gets called, the delayPeriod is assigned the value 100
  • However, if we place the keyword static in front of the variable, the program works
  • Variables declared static are only created and initialized the first time a function is called
  • We can see the use of a static variable in the following example

Example Program with no Global Variables

const int ledPin = 13; void setup() { pinMode(ledPin, OUTPUT); } void loop() { static int delayPeriod = 100; blinkLED(delayPeriod); delayPeriod += 50; if (delayPeriod > 1000) { delayPeriod = 100; // start over } } void blinkLED(int delayPeriod) { digitalWrite(ledPin, HIGH); delay(delayPeriod); digitalWrite(ledPin, LOW); delay(delayPeriod); }

More Information

Summary

  • Constants are a variable that cannot change after it is first assigned a value
  • To declare a constant, we use the keyword: const
    const int MY_CONST = 1;
  • Constants are used to apply meaningful names to numbers
  • Another reason to use named constants is that it is easier to change the value of the number
  • Sometimes we need to remember the value of a variable from one loop() to the next
  • Static variables are a way to limit such variables to a function scope and avoid global variables

Exercise 2: Palindrome Detection Function

In this exercise we'll simplify the while loop from the last project using a function.

Parts

  • Arduino board
  • USB cable

Starter Code 

Start with the code from Part 1 of the last project. 

Specifications

  • Write a function that:
    • Takes a string as an argument
    • returns a bool 
  • The function should have the for loop that detects palindromes.
  • Update Part 2 of your project using your function. 
  • Save your code as palindrome_function.ino 
  • Submit your code with the next assignment.

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
Comments