Kodewick
Lesson 8 of 30Contents

Functions

Lesson 8 of 30 · Part 2 · Control flow and functions · about 15 minutes

When the same code is needed in several places, you don't copy it: you give it a name, make it a function, and call it wherever you need it. main is a function itself; now you'll write your own.

main.cpp
1#include <iostream>2 3double with_tax(double price) {4    return price * 1.08;5}6 7int main() {8    std::cout << with_tax(100) << '\n';9    std::cout << with_tax(250) << '\n';10    return 0;11}
Output
108
270

with_tax takes a price, works out the price with tax, and hands it back. main calls it twice, with a different price each time.

Defining and calling

A function definition has four parts:

PartIn the example above
Return type: the type of the value handed backdouble
Namewith_tax
Parameters: in the parentheses, the values a call must givedouble price
Body: in the curly braces, the code a call runsreturn price * 1.08;

A function that hands nothing back has the return type void. A function that needs nothing has empty parentheses:

main.cpp
1#include <iostream>2 3void line() {4    std::cout << "----------\n";5}6 7int main() {8    line();9    std::cout << "Shopping list\n";10    line();11    return 0;12}
Output
----------
Shopping list
----------

On a call, the program jumps into the function, runs it from top to bottom, then comes back to where it was called and carries on. Press Step to watch it jump in and out. The parentheses are needed: line; on its own does nothing.

Parameters

Parameters let the same function do something slightly different each time. Separate them with commas, each with its type; a call gives the values in the same order:

main.cpp
1#include <iostream>2#include <string>3 4void show_item(std::string name, int count, double price) {5    std::cout << count << " x " << name << ": $" << count * price << '\n';6}7 8int main() {9    show_item("Milk", 2, 6.5);10    show_item("Bread", 1, 12);11    return 0;12}
Output
2 x Milk: $13
1 x Bread: $12

The values given in the call ("Milk", 2, 6.5) are the arguments; they go, in order, into the parameters name, count and price. The wrong number of them, or a type that doesn't fit, is a compile error.

Return values

return does two things: it hands a value back to where the function was called, and it ends the function right away. The value handed back can be stored in a variable, used in a calculation, or shown directly:

main.cpp
1#include <iostream>2 3int bigger(int a, int b) {4    if (a > b) {5        return a;6    }7    return b;8}9 10int main() {11    int best = bigger(78, 91);12    std::cout << best << '\n';13    std::cout << bigger(5, 3) + bigger(2, 4) << '\n';14    return 0;15}
Output
91
9

When a > b is true, return a; ends the function, and return b; never runs.

Guess first

What does this program show?

1#include <iostream>2 3int twice(int n) {4    return n * 2;5    std::cout << "Done\n";6}7 8int main() {9    std::cout << twice(twice(3)) << '\n';10    return 0;11}

Declare first, define later

The compiler reads the code from the top down. If main calls a function written below it, the compiler doesn't know the name yet when it reaches the call:

main.cpp
1#include <iostream>2 3int main() {4    std::cout << square(4) << '\n';5    return 0;6}7 8int square(int n) {9    return n * n;10}
Output
main.cpp: In function 'int main()':
main.cpp:4:18: error: 'square' was not declared in this scope
    4 |     std::cout << square(4) << '\n';
      |                  ^~~~~~

'square' was not declared in this scope: at this point, square hasn't been declared. Either move the function above main, or put a declaration (also called a prototype) at the top: just the return type, name and parameters, ending with a semicolon, no body:

main.cpp
1#include <iostream>2 3int square(int n);4 5int main() {6    std::cout << square(4) << '\n';7    return 0;8}9 10int square(int n) {11    return n * n;12}
Output
16

The declaration tells the compiler "there is a function like this", and the body can come later. With many functions, list the declarations at the top and main right after, so a reader sees at once what the program does.

Pass by value

When you call a function, the argument's value is copied into the parameter. Changing the parameter inside the function changes the copy; the variable outside is untouched:

main.cpp
1#include <iostream>2 3void add_bonus(int score) {4    score += 10;5    std::cout << "In the function: " << score << '\n';6}7 8int main() {9    int score = 80;10    add_bonus(score);11    std::cout << "In main: " << score << '\n';12    return 0;13}
Output
In the function: 90
In main: 80

Both variables are called score, but they are two different variables. Press Step and watch: when add_bonus is called, a new layer appears, the call's own space, with its own score. Adding 10 in the function changes only the score in that layer; when the function ends, the layer is gone, and the score in main stays 80 all along.

To keep a function's result, return it and store it where you called it:

main.cpp
1#include <iostream>2 3int add_bonus(int score) {4    return score + 10;5}6 7int main() {8    int score = 80;9    score = add_bonus(score);10    std::cout << score << '\n';11    return 0;12}
Output
90

(Lesson 10 shows another way: references.)

Returning a std::string

A function can return any type, std::string included. Put a decision in a function, and the code that uses it reads clearly:

main.cpp
1#include <iostream>2#include <string>3 4std::string grade_of(int score) {5    if (score >= 90) {6        return "Excellent";7    } else if (score >= 60) {8        return "Pass";9    }10    return "Fail";11}12 13int main() {14    std::cout << "Ana: " << grade_of(95) << '\n';15    std::cout << "Ben: " << grade_of(72) << '\n';16    std::cout << "Cal: " << grade_of(48) << '\n';17    return 0;18}
Output
Ana: Excellent
Ben: Pass
Cal: Fail

A missing return

In a function whose return type isn't void, every path must end with return. Miss one and g++ gives a warning (warning:), shown above the output. A warning doesn't stop the compile, and the program still runs:

main.cpp
1#include <iostream>2 3int sign(int n) {4    if (n > 0) {5        return 1;6    } else if (n < 0) {7        return -1;8    }9}10 11int main() {12    int a = sign(5);13    int b = sign(0);14    std::cout << a << " " << b << '\n';15    return 0;16}
Output
main.cpp: In function 'int sign(int)':
main.cpp:9:1: warning: control reaches end of non-void function [-Wreturn-type]
    9 | }
      | ^
control reached the end of 'sign' without a return value

What happened: Stopped at line 9. The function ended without returning a value.

control reaches end of non-void function: the function got to its end without a return. sign(5) is fine; for sign(0) neither condition is true, the function hands nothing back, and that is undefined behavior: the program goes wrong right there, before showing anything. Don't skip warnings: add return 0; at the end and run it again.

Practice

Write the code, then press Check. A hint costs nothing; the answer is there when you need it.

Exercise 1

Area of a rectangle

Write a function area that takes a width and a height (both int) and returns the area. main is already written; the program should show Area: 24.

It should print
Area: 24
exercise1.cpp
1#include <iostream>2 3int main() {4    std::cout << "Area: " << area(6, 4) << '\n';5    return 0;6}
Output
Press Run or Ctrl+Enter
Exercise 2

Celsius to Fahrenheit

Write a function to_fahrenheit that takes degrees Celsius (double) and returns degrees Fahrenheit (Celsius × 1.8 + 32). Show 25 C = 77 F.

It should print
25 C = 77 F
exercise2.cpp
1#include <iostream>2 3int main() {4    double c = 25;5 6    return 0;7}
Output
Press Run or Ctrl+Enter
Exercise 3

Fix it: the restock that didn't stick

restock is meant to add 20 to the stock, but stock in main is still 5. Without references, fix it so the program shows Stock: 25.

It should print
Stock: 25
exercise3.cpp
1#include <iostream>2 3void restock(int stock) {4    stock += 20;5}6 7int main() {8    int stock = 5;9    restock(stock);10    std::cout << "Stock: " << stock << '\n';11    return 0;12}
Output
Press Run or Ctrl+Enter

What you learned

  • A function = return type + name + parameters + body; one that hands nothing back is void. Call it with its name and parentheses, giving the arguments.
  • return hands back a value and ends the function at once.
  • The compiler reads from the top: define a function before calling it, or declare it first with a prototype, int square(int n);.
  • Pass by value: the argument is copied into the parameter, so changing the parameter doesn't change the variable outside; to keep a result, return it.
  • A non-void function must return on every path; missing one gives the warning control reaches end of non-void function. Don't skip warnings.