Functions
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:
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}
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:
| Part | In int square(int n) | What it is |
|---|---|---|
| Return type | int | The type of the value the function hands back |
| Name | square | What 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.
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}
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:
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}
***** ### --------
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:
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}
16 34
A function can have more than one return. It ends at whichever one it reaches first:
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}
12 30 4.5
A function whose return type is double hands back a decimal number:
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}
a + b is 15. Dividing by 2.0 (a decimal) gives 7.5; this is not integer division. The return type is double, so 7.5 comes back as it is, and %.1f prints 7.5.
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:
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}
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:
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}
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:
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}
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:
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}
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.
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.
27 125
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}
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.
60.00 45.00
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}
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.
Balance: 150
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}
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;
voidmeans no return value, or no parameters. returnhands 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.