Functions

A function is a named block of code that does one job. Functions let you reuse code, give names to steps, and break a big program into small pieces you can understand and test one at a time. Every C program is built from them, starting with main.

Defining and calling

C
#include <stdio.h>

void print_divider(void) {
    printf("----------------\n");
}

int main(void) {
    print_divider();
    printf("Monthly report\n");
    print_divider();
    return 0;
}
Output
----------------
Monthly report
----------------

A definition has a return type (void means it returns nothing), a name, a parameter list in parentheses, and a body. Calling it runs the body, then returns to where you called from.

Parameters and return values

C
#include <stdio.h>

double area_of_rectangle(double width, double height) {
    return width * height;
}

int main(void) {
    double kitchen = area_of_rectangle(4.2, 3.0);
    double hall = area_of_rectangle(6.0, 1.2);
    printf("Kitchen %.2f m2, hall %.2f m2, total %.2f m2\n",
           kitchen, hall, kitchen + hall);
    return 0;
}
Output
Kitchen 12.60 m2, hall 7.20 m2, total 19.80 m2

width and height are parameters; 4.2 and 3.0 are the arguments passed in. return sends a value back and ends the function immediately.

Declaring before use: prototypes

The compiler reads top to bottom, so a function must be declared before it's called. If you'd like main first, put a prototype — the function's header followed by a semicolon — above it:

C
#include <stdio.h>

int square(int n);          // prototype
void greet(const char *name);

int main(void) {
    greet("Ada");
    printf("7 squared is %d\n", square(7));
    return 0;
}

int square(int n) {
    return n * n;
}

void greet(const char *name) {
    printf("Hello, %s!\n", name);
}
Output
Hello, Ada!
7 squared is 49

Prototypes are also what header files contain (lesson 52). Calling a function that hasn't been declared at all is an error in modern C:

C
#include <stdio.h>

int main(void) {
    printf("%d\n", cube(3));
    return 0;
}

int cube(int n) {
    return n * n * n;
}
Compiler error
main.c:4:20: error: call to undeclared function 'cube'; ISO C99 and later do not support implicit function declarations [-Wimplicit-function-declaration]

(void) means "no parameters"

Write int main(void) and void print_divider(void), not empty parentheses. In C17 and earlier, () in a declaration means "parameters not specified", which turns off argument checking. (C23 finally makes () mean the same as (void).)

Returning early

C
#include <stdio.h>

const char *describe_temperature(double celsius) {
    if (celsius < 0) return "freezing";
    if (celsius < 15) return "cold";
    if (celsius < 25) return "mild";
    return "hot";
}

int main(void) {
    double temps[] = { -3, 9, 18, 31 };
    for (int i = 0; i < 4; i++) {
        printf("%5.1f: %s\n", temps[i], describe_temperature(temps[i]));
    }
    return 0;
}
Output
 -3.0: freezing
  9.0: cold
 18.0: mild
 31.0: hot

A function with a non-void return type must return a value on every path. Falling off the end without one is a bug that -Wall reports.

Designing good functions

  • One job each. If you need "and" to describe it, consider splitting it.
  • Name it after what it does: count_vowels, is_valid_email, print_invoice. Functions answering yes/no questions often start with is_ or has_ and return bool.
  • Keep them short — a screenful at most is a good guide.
  • Prefer parameters and return values over global variables, so each function's inputs and outputs are visible.

Try it yourself

Write is_prime returning bool, and use it to count the primes below 100.

Try it yourself · main.c
Edit the code, copy it into main.c, then compile and run it with cc -std=c17 -Wall main.c -o main && ./main. How to set up a C compiler
Output
25 primes below 100