Kodewick
Lesson 8 of 30Contents

Functions

Lesson 8 of 30 · Part 2 · Functions and arrays · about 15 minutes

When you need the same few lines in several places, you can make them a function: give them a name, and use the name whenever you need them. You have been using functions all along: printf is one, and so is main. Here is your first function of your own:

main.c
1#include <stdio.h>2 3void say_hello(void) {4    printf("Hello!\n");5    printf("Come on in.\n");6}7 8int main(void) {9    say_hello();10    say_hello();11    return 0;12}
Output
Hello!
Come on in.
Hello!
Come on in.

say_hello is written once and used twice. say_hello(); in main is a call: the program jumps into the function, runs its statements, then comes back to where it was called and carries on.

Defining a function

A function has these parts:

PartIn int square(int n)What it is
Return typeintThe type of the value the function hands back
NamesquareWhat you call it by; same naming rules as variables
Parameters(int n)The values you give it when you call it, each with a type
Body{ ... }What the function does, inside curly braces

say_hello above has the return type void, which means "hands nothing back". The void in its parentheses means "takes no parameters", just like in int main(void).

Parameters

Parameters let one function do something a little different each time. The minutes in the definition is a parameter; the value you put in the parentheses when you call it is an argument. On each call, the argument's value goes into the parameter.

main.c
1#include <stdio.h>2 3void print_time(int minutes) {4    printf("%d h %d min\n", minutes / 60, minutes % 60);5}6 7int main(void) {8    print_time(135);9    print_time(90);10    print_time(45);11    return 0;12}
Output
2 h 15 min
1 h 30 min
0 h 45 min

A function can take several parameters, separated by commas, each with its own type. The arguments match them one by one, in order:

main.c
1#include <stdio.h>2 3void print_line(int count, char symbol) {4    for (int i = 0; i < count; i++) {5        printf("%c", symbol);6    }7    printf("\n");8}9 10int main(void) {11    print_line(5, '*');12    print_line(3, '#');13    print_line(8, '-');14    return 0;15}
Output
*****
###
--------

Return values

return hands a value back to wherever the function was called, and ends the function. You can store the value in a variable or use it right away:

main.c
1#include <stdio.h>2 3int square(int n) {4    return n * n;5}6 7int main(void) {8    int a = square(4);9    printf("%d\n", a);10    printf("%d\n", square(3) + square(5));11    return 0;12}
Output
16
34

A function can have more than one return. It ends at whichever one it reaches first:

main.c
1#include <stdio.h>2 3int larger(int a, int b) {4    if (a > b) {5        return a;6    }7    return b;8}9 10int main(void) {11    printf("%d\n", larger(7, 12));12    printf("%d\n", larger(30, 5));13    printf("%.1f\n", larger(3, 9) / 2.0);14    return 0;15}
Output
12
30
4.5

A function whose return type is double hands back a decimal number:

Guess first

What does this print?

1#include <stdio.h>2 3double average(int a, int b) {4    return (a + b) / 2.0;5}6 7int main(void) {8    printf("%.1f\n", average(7, 8));9    return 0;10}

Declare before you use

A C compiler reads your code from top to bottom. When it reaches a call, it must already know what the function looks like: its name, how many parameters of what types, and what it returns. If the function comes after main, the compiler hasn't met it yet at the call:

main.c
1#include <stdio.h>2 3int main(void) {4    printf("%.1f\n", half(5));5    return 0;6}7 8double half(int n) {9    return n / 2.0;10}
Output
main.c: In function 'main':
main.c:4:22: warning: implicit declaration of function 'half' [-Wimplicit-function-declaration]
    4 |     printf("%.1f\n", half(5));
      |                      ^~~~
main.c:4:16: warning: format '%.1f' expects argument of type 'double', but argument 2 has type 'int' [-Wformat=]
    4 |     printf("%.1f\n", half(5));
      |             ~~~^     ~~~~~~~
      |                |     |
      |                |     int
      |                double
      |             %.1d
main.c: At top level:
main.c:8:8: error: conflicting types for 'half'; have 'double(int)'
    8 | double half(int n) {
      |        ^~~~
main.c:4:22: note: previous implicit declaration of 'half' with type 'int()'
    4 |     printf("%.1f\n", half(5));
      |                      ^~~~

Read it from the top. On line 4 the compiler meets half for the first time and gives a warning: the function hasn't been declared, so it guesses that it returns int. On line 8 it finds that half really returns double, which doesn't match its guess. That is the error. The warning about %.1f in between comes from the same guess: the compiler thinks half(5) is an int. (From version 14 on, gcc treats the first warning as an error already.)

There are two fixes: move the function above main, or write a function prototype (also called a function declaration) near the top that says what it looks like, and keep the function itself further down:

main.c
1#include <stdio.h>2 3double half(int n);4 5int main(void) {6    printf("%.1f\n", half(5));7    return 0;8}9 10double half(int n) {11    return n / 2.0;12}
Output
2.5

A prototype is the first line of the function with a semicolon after it, and no body. Once you have many functions, a common layout is all the prototypes at the top, then main, then the other functions. In fact, what #include <stdio.h> brings in is the prototypes of printf and friends.

Pass by value

When you call a function, it gets the argument's value. The parameter is a new variable holding a copy of that value. So when the function changes its parameter, it changes the copy, and the caller's variable stays the same:

main.c
1#include <stdio.h>2 3void add_bonus(int score) {4    score = score + 10;5    printf("In the function: %d\n", score);6}7 8int main(void) {9    int score = 80;10    add_bonus(score);11    printf("In main: %d\n", score);12    return 0;13}
Output
In the function: 90
In main: 80

Both variables are named score, but they are two different variables. Press Step to watch: when add_bonus is called, a new box appears in memory. This is the call's stack frame, and it holds its own score. Adding 10 changes only the score in that box. When the function ends, the box goes away, and the score in main's box is still 80.

To change the caller's variable, the simplest way is to have the function return the new value, and let the caller store it:

main.c
1#include <stdio.h>2 3int add_bonus(int score) {4    return score + 10;5}6 7int main(void) {8    int score = 80;9    score = add_bonus(score);10    printf("In main: %d\n", score);11    return 0;12}
Output
In main: 90

Tip: A function can change the caller's variable directly too, but that takes pointers, which come in lesson 16.

Practice

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

Exercise 1

Cube

Write a function cube that takes an int and returns its cube (the number times itself three times). The program should print 27 and 125, each on its own line.

It should print
27
125
exercise1.c
1#include <stdio.h>2 3// write cube here4 5int main(void) {6    printf("%d\n", cube(3));7    printf("%d\n", cube(5));8    return 0;9}
Output
Press Run or Ctrl+Enter
Exercise 2

Discount

Write a function discount that takes a price price (double) and a discount percentage percent (int) and returns the price after the discount. For example, discount(80.0, 25) is 80 minus 25%, which is 60. The program should print 60.00 and 45.00.

It should print
60.00
45.00
exercise2.c
1#include <stdio.h>2 3double discount(double price, int percent) {4 5}6 7int main(void) {8    printf("%.2f\n", discount(80.0, 25));9    printf("%.2f\n", discount(50.0, 10));10    return 0;11}
Output
Press Run or Ctrl+Enter
Exercise 3

The deposit that vanished

deposit is meant to add money to a balance, but the balance in main stays at 100. Fix it: make deposit return the new balance, and have main store it back in balance. The program should print Balance: 150.

It should print
Balance: 150
exercise3.c
1#include <stdio.h>2 3void deposit(int balance, int amount) {4    balance = balance + amount;5}6 7int main(void) {8    int balance = 100;9    deposit(balance, 50);10    printf("Balance: %d\n", balance);11    return 0;12}
Output
Press Run or Ctrl+Enter

What you learned

  • A function gives a piece of code a name: write it once, call it anywhere. After the call, the program carries on where it left off.
  • A definition has a return type, a name, parameters (each with a type) and a body; void means no return value, or no parameters.
  • return hands back a value and ends the function.
  • The compiler reads top to bottom: a function must be declared before it is called. Put it first, or write a prototype.
  • C passes by value: a parameter is a copy of the argument, so changing it doesn't change the caller's variable. To change it, return the new value.