Concepts / Declaring and Defining Functions in C++

Declaring and Defining Functions in C++

C++ programs get their work done mainly by calling functions.

  • Programming
Interactive lab

Try it: Defining and Calling Functions

What def and a call really do: def only creates a function object, a call binds the arguments to the parameters in a new frame and runs the body, return ends the call at once and its value replaces the call expression in the caller — while a function that only prints gives back None.

How it works

  1. def name(params): creates a function object and binds the name to it; the body does not run yet.
  2. A call evaluates its arguments, opens a new frame, binds each parameter to its argument and runs the body from the top.
  3. return value ends the call immediately (lines after it never run) and sends the value back; the frame and its local names are discarded.
  4. In the caller, the call expression is replaced by the returned value, which can be stored, used in a larger expression or passed to another call.
  5. A body that ends without return (e.g. one that only prints) makes the call evaluate to None — so x = f() stores None and None + 1 raises TypeError.
  6. Calling a name before its def has run raises NameError: the function does not exist yet.

Default run (18 steps): The program is ready. Python runs it from the top, one statement at a time. Idea: the value a call returns is what the caller stores. … The program finished. Console: 8.

Simplified: Four fixed, hand-written programs; you edit the literal arguments, switch the body between return and print, and move the def after the calls. The lab's own small evaluator runs them (your input is never executed as code); every variant was run in CPython 3.12 and the lab reproduces its sys.settrace line/call/return/exception events, its output and its error message. Functions are shown as <function name> without a memory address.

Educational simulation

Loading the simulation…

Why Programs Are Built From Named Pieces

Most C++ programs are not one long list of instructions. Instead, the work is split into named pieces, and the program gets things done by calling those pieces at the right moments. Each of these pieces is a function. When you read a C++ program, much of what you are reading is a set of calls: one function asks another function to do a job, and waits for that job to finish.

Because almost everything is done through calls, two questions come up again and again. First, how does the program know that a named function exists before it tries to use it? Second, when a function finishes, how does the code that called it know whether anything came back? This article answers both questions. The first is answered by declarations and definitions, and the second by the return type, which is the single idea that tells a reader how a function reports back to its caller.

Following Control Through One Call

Before looking at declarations, it helps to watch what happens at run time when one function calls another. Read the program below. Execution begins in main. When main reaches the line that calls add, control leaves main and moves into add. The body of add runs, produces a result, and control returns to main at the line right after the call. Then main continues with the next statement.

cpp
Output
5
Notes on the code
  • Line 3 is a declaration of add: it gives the name and shape without a body
  • Line 6 is the call: control jumps into add with a set to 2 and b set to 3
  • Line 12 is the definition of add: the body that actually computes a + b
  • After add returns 5, main resumes at the next line and prints it
main()reaches the call to addadd(2, 3)body runs: return a + bmain() resumesline after the callcallreturns 5
When main() calls add(2, 3), where does execution go next, and how does it return to the line after the call?

Notice what the diagram shows. The call does not end the program and does not skip the rest of main. It is a temporary handoff: control goes into add, and then it comes back to the place the call was made. A program built from many functions is really a chain of these handoffs, each one happening at a moment the code chooses.

Declaring Versus Defining a Function

A function has two separate roles in a program, and C++ keeps them apart. Declaring a function makes the function known to the code that uses it. Defining a function specifies how its operation is to be done. A declaration tells the reader what the function is called and what shape it has. A definition supplies the body that performs the work.

Definition

A declaration introduces a function's name and shape so that code written earlier can refer to it correctly. In the example above, the line int add(int a, int b); is a declaration: it ends with a semicolon and has no body.

Definition

A definition specifies how the function's operation is carried out. In the example above, int add(int a, int b) followed by a braced body containing return a + b; is the definition.

int add(int a, intb);signature only, ends with asemicolonint add(int a, int b){ ... }signature followed by abodyName and shape knowncode can refer to addreturn a + bthe operation itself
How does a declaration (signature only) differ from a definition (body), and what role does each one play for the code that calls add?

The rule that ties these together is simple. A function cannot be called unless it has been previously declared, so the declaration must appear earlier in the program than the call. The compiler relies on this: when the code says to call a function, that function must already be known to the program. A helpful way to hold the two ideas apart is to think of a declaration as an entry in a list of available tools, which lets other code refer to each tool by name, and the definition as the instructions for how each tool works.

Reading the Parts of a Function Header

The first line of a function, before its braces, is often called its header or signature. Reading it from left to right tells you most of what you need to know about how the function is used. Every header begins with a return type, followed by the function's name, followed by a parameter list in parentheses.

intreturn typeaddfunction name(int a, int b)parameter list
Which part of the header is the return type, which is the name, and which is the parameter list?

The return type is the first word, and it is the one to read first. It answers the key question before you look inside the body: does this function give a value back to the code that called it? The name is how other code refers to the function, and the parameter list describes what information the caller supplies when it makes the call.

Return Types and What Travels Back

Every function has a return type, and that type tells you whether the function gives a value back to the code that called it. The return type is part of the shape of the function, so it is visible in the header even before you read the body. If the return type says void, the function does not return a value, and the caller receives nothing back from the call. If the return type names a kind of value, such as int, the function is designed to give a value back, and the caller can use that value.

Here is what that means for the path a result takes. When a function returns a value, the calling code makes the call, the function body performs its operation, and the value comes back to the place where the call was made. That place is where the value can be stored or used. In the add example, the value 5 travels from add back to main and is stored in total. A function declared void has no such value to carry, so there is nothing to store.

cpp
Output
Hello
Notes on the code
  • greet has return type void, so it hands nothing back to its caller
  • The call greet(); stands alone on its own line because there is no value to use

Worked Reading of Three Headers

Worked example

Deciding Whether Each Header Returns a Value

For each header, decide whether the function hands a value back to its caller, and state what the caller can do with the result.

  1. 1
    Header one: double half(double x);

    The return type is double, which names a kind of value. The function is designed to give a double back, so the caller can store it, for example in a double variable, or use it in an expression.

  2. 2
    Header two: void log_message(int code);

    The return type is void. The function does not hand a value back, so the caller receives nothing from the call. The call stands alone as a statement.

  3. 3
    Header three: bool is_even(int n);

    The return type is bool, which names a kind of value. The caller gets a true or false answer back, and can use it in a condition such as an if statement.

Result

Only header two is void and hands nothing back. Headers one and three both name value types, so the caller can use what comes back. The return type alone answered the question without reading any body.

Mistakes With Declarations and Return Types

The mistakes below cluster around two rules from this article: a function must be declared before it is called, and the return type decides whether a result exists to be used.

  • Common mistake

    Calling a function before it has been declared

    What it looks like

    A call to add(2, 3) appears inside main, but no declaration of add appears above main and the definition of add comes only further down the file.

    Why it fails

    A function cannot be called unless it has been previously declared. The compiler needs the name and shape to be known before the call appears in the code.

    Do this instead

    Place the declaration of add earlier in the file, such as on a line above main, so that the call comes after it.

  • Common mistake

    Using the result of a void function

    What it looks like
    int x = greet(); where greet has return type void.
    Why it fails

    A return type of void means the function does not give a value back, so there is no value to store in x.

    Do this instead

    Call greet(); on its own line. If a result is genuinely needed, the function's return type should name the kind of value it produces.

  • Common mistake

    Assuming a declaration supplies the operation

    Why it fails

    A declaration only introduces the name and shape. The definition is where the operation is specified, so a declaration by itself does not say what the function does.

Practice Reading Return Types

EASY

Given the two declarations int square(int n); and void print_square(int n);, which function hands a value back to its caller, and what could the caller do with that value? Then explain what the caller of the other function can and cannot do with the result of its call.

Hints
  • Look only at the return type, the first word in each header
  • A return type naming a value type means the caller can store or use the result
  • A void return type means nothing comes back, so there is no result to store

Key Takeaways

  • C++ programs get their work done mainly by calling functions, and each call hands control into a named piece of code and then returns control to the line after the call.
  • A function must be declared before it is called, so the declaration comes earlier in the program than the call.
  • A declaration makes a function's name and shape known to the code that uses it, while a definition specifies how the operation is carried out.
  • The return type in a function header tells you whether a value comes back to the caller: void means nothing is returned, and any named value type means a value is handed back for use.
  • Two common mistakes are calling a function before its declaration and trying to use the result of a void function, so read the declaration order and the return type before writing a call.

Related concepts

More concepts starting with D