Concepts / C++ Function Definitions

C++ Function Definitions

A function's interface names the function, its return type, and its parameters.

  • 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…

Predicting a Program's Output

A C++ program is often split into named pieces called functions. Each piece does one job, and other pieces can call it when they need that job done. Before learning the rules, try reading a small program and predicting what it does. The program below has two function definitions and a main function that calls one of them.

cpp
Notes on the code
  • square is a value-returning function: its return type is double, so it sends a double back to its caller.
  • print_square has return type void, so it sends nothing back, but its body still writes output.
  • main calls print_square with the argument 1.234.

What do you think happens?

Before reading on, predict exactly what the program prints when main calls print_square with 1.234.

Reveal answer
Answer:

1.52276

square receives 1.234 as x and returns x times x, which is $1.234 \times 1.234 = 1.522756$. print_square sends that value to the output stream with cout. With default formatting, cout shows six significant digits, so the printed line is 1.52276.

Reading the Parts of a Definition

Definition

A function's interface names the function, its return type, and its parameters. The interface is the part of the definition that a caller needs to know in order to use the function.

Take the first line of square from the program above: double square(double x). Read it from left to right. The first word, double, is the return type, which declares what kind of value the function sends back to its caller. The next word, square, is the name that other code uses to call the function. The part in parentheses, (double x), is the parameter list. It declares the input the function expects, here one input named x of type double. The body is the block between the curly braces, and it holds the statements that do the work.

doublereturn type: the kind ofvalue sent backsquarefunction name used bycallers(double x)parameter list: input x oftype double
Which part of the line double square(double x) is the return type, which is the name, and which is the parameter list?

The interface and the body play different roles. A caller only needs the interface to know how to call the function and what comes back. The body is the code that carries out the work, and the caller does not need to read it to use the function.

Value-Returning Functions Versus void

The return type decides whether a function hands a result back to its caller. In square, the return type is double, and the body ends with return x * x; so the function computes a value and sends it back. In print_square, the return type is void, which means the function sends nothing back. A void function can still do work and write output in its body, which is exactly what print_square does with cout.

FeatureValue-returning function (square)void function (print_square)
Return type in the definitiondoublevoid
What the caller receivesThe value named in the return statement, here x times xNo value
Work done in the bodyComputes and returns a valueWrites output with cout
How the caller uses itCan place the result in an expression, such as cout << square(x)Calls it as a statement, such as print_square(1.234);

Two kinds of function definitions in the same program

cpp
Notes on the code
  • The return statement sends x * x back to the place where square was called.
  • The caller stores that returned value in area.

Tracing main, print_square, and square

The printed line in the first program is produced through a chain of calls. main calls print_square. While building its output, print_square calls square and uses the value that square returns. To find where a result comes from, follow the chain in order: the value main passes in, the calculation and return inside square, and the use of that result inside print_square. The diagram below shows which function is active at each point, going down the calls and back up the returns.

maincalls print_square(1.234)print_squarex is 1.234; calls square(x)squarecomputes x times x =1.522756call with 1.234call with xreturns 1.522756returns void, prints line
In what order do main, print_square, and square run, and which function is active at each point as control goes down and comes back up?
Worked example

Tracing the program line by line

Trace what happens from the moment main calls print_square(1.234) until the program prints its line and ends.

  1. 1
    Step 1: main makes the call

    main calls print_square and passes 1.234 as the argument. The parameter x inside print_square receives 1.234.

  2. 2
    Step 2: print_square needs a value

    The statement std::cout << square(x) must evaluate square(x) before it can print anything, so print_square calls square with x.

  3. 3
    Step 3: square computes and returns

    square runs return x * x. With x equal to 1.234, this is $1.234 \times 1.234 = 1.522756$, and that value is returned to print_square.

  4. 4
    Step 4: print_square writes output

    print_square sends the returned value to cout. With default formatting, the line shown is 1.52276.

  5. 5
    Step 5: control returns to main

    print_square has return type void, so nothing comes back. Control returns to main, which moves on to return 0.

Result

The program prints 1.52276. The value flowed from main into print_square, from print_square into square, and back from square into print_square for printing.

Mistakes Beginners Make Reading Definitions

  • Common mistake

    Treating the return type as the function's name

    What it looks like

    Reading double square(double x) and thinking the function is called double.

    Why it fails

    The return type comes first and describes the kind of value sent back. The name comes after it.

    Do this instead

    Identify the name as the word directly before the parameter list, here square.

  • Common mistake

    Expecting a void function to give back a number

    What it looks like

    Writing double r = print_square(2.0); and expecting r to hold the squared value.

    Why it fails

    A void function sends nothing back to its caller, so there is no value to store.

    Do this instead

    Call a void function as a statement, such as print_square(2.0);, and let its body do the output.

  • Common mistake

    Assuming the output comes from main alone

    What it looks like

    Looking only at main and guessing the printed line without reading print_square or square.

    Why it fails

    The printed value is built by a chain of calls, so the calculation happens inside square.

    Do this instead

    Follow the chain: check the argument passed by main, the calculation and return in square, then the use of that result in print_square.

  • Common mistake

    Confusing the parameter with the argument

    What it looks like

    Thinking that x in (double x) is the value 1.234.

    Why it fails

    The parameter x is a placeholder declared in the definition. The value 1.234 is the argument supplied by the caller at the call site.

    Do this instead

    Read the parameter list as a declaration of what input is expected, and read the call print_square(1.234) as the place where the actual value is supplied.

Practice Tracing a Changed Function

MEDIUM

Keep the same main and print_square as above, but change square so that its body is return x + x; instead of return x * x;. Without running the program, predict the line that is printed when main calls print_square(1.234). Then name the return type of square and say whether print_square sends a value back to main.

Hints
  • Start by following the call from main to print_square, then from print_square to square.
  • Only the calculation inside square changed. Work out what x plus x equals when x is 1.234.
  • Remember that print_square has return type void, so it never hands a value to main.

Key Takeaways

  • A function definition's interface names the function, its return type, and its parameters; the body holds the statements that do the work.
  • In the line double square(double x), double is the return type, square is the name, and (double x) is the parameter list.
  • A value-returning function such as square sends a value back to its caller through a return statement, while a void function such as print_square sends nothing back but can still write output.
  • To trace a result, follow the calls from main through each function that calculates or uses the value, and then follow the returns back up the chain.
  • When a result looks wrong, check the value passed in, the calculation and return inside the called function, and how the caller uses that returned result.

Related concepts

More concepts starting with C