Contact
Evil Genius SocietyHomeLearnForumLeaderboardSocialsMessages
← Back to module

Lesson 1 of 8

A function gives a name to a piece of work so you can do it from several places without writing it out again.

#include <iostream>

int add(int a, int b) {
    return a + b;
}

int main() {
    std::cout << add(2, 3) << "\n";
    std::cout << add(10, -4) << "\n";
    return 0;
}
5
6

Reading int add(int a, int b):

  • int — the return type: what this function hands back.
  • add — the name.
  • (int a, int b) — the parameter list: what it needs, and what those values are called inside.
  • { ... } — the body: the work itself.

Declaration and definition

You met these words in lesson 2.6 when linker errors were the subject. Now they matter directly.

A declaration states the function's interface and ends in a semicolon:

int add(int a, int b);

A definition supplies the body:

int add(int a, int b) {
    return a + b;
}

Every definition is also a declaration. The reverse isn't true, and that asymmetry is the whole point:

  • The compiler needs only a declaration to check a call. It verifies the argument types and the use of the result, then emits "call whatever add turns out to be."
  • The linker needs the definition, to fill in an actual address.

That's why a declaration with no matching definition compiles fine and fails at link time — the error from lesson 2.6.

Why separate them at all?

Because it lets you use a function before you've written it, and — more importantly — lets you publish an interface without shipping the implementation. That's exactly what a header file is (lesson 5.8), and it's how the standard library works: #include <iostream> pastes in declarations, and the compiled library supplies the definitions at link time.

void: a function that returns nothing

#include <iostream>
#include <string>

void greet(const std::string& name) {
    std::cout << "Hello, " << name << "!\n";
}

int main() {
    greet("Ada");
    greet("Grace");
    return 0;
}
Hello, Ada!
Hello, Grace!

void means "no return value." You can still return; early to leave the function; you just can't return a value.

Include what you use. Delete the #include <string> above and it very likely still compiles on your machine — <iostream> happens to pull <string> in indirectly on many implementations. Don't rely on that. It's not guaranteed by the standard, and the day it stops being true (a compiler upgrade, a different platform) you get a wall of errors in a file you never touched. If you name a type, include its header.

Every path must return

// WILL NOT COMPILE
int classify(int n) {
    if (n > 0) {
        return 1;
    }
    // falls off the end when n <= 0
}

With -Wall this is a warning, and reaching that end is undefined behaviour. Make every path explicit:

#include <iostream>

int classify(int n) {
    if (n > 0) {
        return 1;
    } else if (n < 0) {
        return -1;
    }
    return 0;
}

int main() {
    std::cout << classify(7) << " " << classify(-7) << " " << classify(0) << "\n";
    return 0;
}
1 -1 0

main is the one exception — it returns 0 implicitly (lesson 2.3).

Why functions are worth it

Beyond avoiding repetition:

  • A name is documentation. is_prime(n) says what a loop full of % does not.
  • They're independently testable. Module 18 relies on this entirely.
  • They limit scope. Variables inside a function can't be touched from outside.
  • They let you change one thing. Fix a bug in add and every caller is fixed.

The practical guideline is that a function should do one thing and be short enough to read without scrolling. If you can't name it without using "and", it's probably two functions.

Try it

Write a function int max_of_three(int a, int b, int c) that returns the largest of its three arguments, and call it with a few sets of values.

Checkpoint

  • A function has a return type, a name, parameters, and a body.
  • A declaration ends in ; and gives the interface; a definition supplies the body.
  • The compiler needs only the declaration; the linker needs the definition.
  • void means no return value; return; still exits early.
  • Every path through a non-void function must return a value.
  • One function, one job, short enough to read at a glance.

Questions about this lesson

Log in to ask a question.