A function is a named block of code that performs one job and can be called from anywhere in a program. Functions allow writing a piece of logic once, naming it, and reusing it instead of pasting the same code in five different places. This lesson covers what a function is, the parts that make one up, how to define and call functions that return a value, and how void functions differ from those that don't.
Consider an online store program that needs to print a welcome banner in three different places: when the app starts, when a user signs in, and when a guest browses the homepage. Without functions, the same three lines of output code get pasted three times. Fixing a typo means fixing it in every copy. Changing the banner means hunting down every paste.
The painful version:
Nine lines of output code, three identical copies of the same three lines. The compiler doesn't care, but a human reader has to scan all of it, and any change to the banner requires editing three places.
A function fixes that. Wrap the three lines once, give them a name, and call the name three times:
Same result, but the banner text lives in one place. Fixing it once fixes it everywhere. That principle has a name: DRY, which stands for "Don't Repeat Yourself." Functions are the most common tool for following it.
Functions also help with decomposition. A program that does a hundred different things is hard to read as one giant main. Split into ten functions of ten things each, the program becomes a series of named steps, and each step can be read independently.
Every function in C++ has the same basic shape. Four parts, in order:
Each part has a job:
void.calculateTotal, not doStuff).().The first line (everything before the opening brace) is the function header. The braces and what's between them are the function body.
A function that adds two prices and gives back the sum:
Reading the header: return type is double, name is addPrices, it takes two double parameters called a and b. The body computes a + b and uses return to hand the result back.
This lesson uses simple parameter lists in examples, but the focus is on the function as a whole, not the parameter mechanics.
The diagram shows the four pieces of the function definition lined up. A function header reads left to right: type, name, inputs, then the body in braces.
Two separate actions involve a function: defining it (writing the body once), and calling it (running it from somewhere else). These often get mixed up because the syntax looks related but is different.
A definition has braces and a body:
A call has parentheses and a semicolon:
The call is what actually runs the code. The definition describes what the code is. A function can be defined once and called as many times as needed.
Two calls, one definition. The body of displayCart runs twice, once per call.
The order of code on the page matters. C++ reads the file top to bottom, and a function must be visible to the compiler at the point where it's called. The simple way to handle this is to define helper functions above main, the same way the examples in this lesson do. For now, "define above the caller" works.
The diagram captures the relationship: one definition, many calls. Each call jumps into the body, runs it, then comes back to the line after the call.
The line that calls a function is the call site. It looks like the function's name followed by parentheses. Empty parentheses if the function takes no inputs, comma-separated values if it does. The semicolon at the end makes the call a statement.
When the program hits a call site, three things happen in order:
return), control jumps back to the line right after the call site.That round trip happens fast, but it's important to picture it. A call isn't text on the page; it's an actual jump in the order code runs.
The "Before ship" line runs, then the call site jumps into shipOrder, the two indented lines run, control jumps back, and "After ship" runs. The two indented lines aren't physically in main, but they show up between the two main lines because that's where the call happens.
A function with a non-void return type must hand back a value. The return keyword does that. When return value; runs, the function ends immediately, and the value gets passed back to the call site.
A function that computes a cart total from a fixed set of prices and returns the sum:
Reading the call site: double total = calculateTotal(); runs calculateTotal, gets a double back, and stores it in the local variable total. The = here isn't comparison or even regular assignment; it's the syntax for "store the function's return value in this variable."
Storing the return value isn't required. It can be used directly in an expression:
Or pass it to another function:
The function call _is_ the value. Anywhere a literal 36.48 would work, calculateTotal() works too, and the program runs the function and uses the result in place.
A variation: a function that returns a discount rate based on a fixed cart size.
Two return statements show up in this function. The first one runs only if the if condition is true. If it does run, the function ends right there, and the second return never executes. The second return only runs when the if condition is false.
That's worth pausing on: return is an immediate exit. The function stops, the value is sent back, and nothing else in the body runs.
voidNot every function needs to give back a value. Some functions exist to do something (print, update, record), not to compute a result. For those, the return type is void, C++'s way of saying "this function returns nothing."
displayCart doesn't compute anything to capture in a variable. It writes to the screen and that's it. The return type is void, the body has no return value; line (because there's no value to return), and the call site doesn't store anything: just displayCart(); on its own line.
Trying to use a void function as if it returned a value is a compile error:
g++ reports something like:
The compiler reports that displayCart doesn't produce a value, so total = displayCart() doesn't make sense.
The two flavors line up like this:
| Aspect | Value-returning function | void function |
|---|---|---|
| Return type | int, double, bool, std::string, etc. | void |
Must use return value;? | Yes, on every path | No (optional bare return;) |
| Can be used in an expression? | Yes (double t = calculateTotal();) | No (call is a statement, not a value) |
| Typical job | Compute and hand back a result | Perform an action with a side effect |
The "side effect" wording is worth unpacking. A side effect is anything the function does besides handing back a value: printing to the screen, modifying a global, writing to a file. A void function exists for its side effects. A value-returning function can also have side effects, but its main contribution is the return value.
return Statement in Depthreturn does two things at the same time: it exits the function immediately, and (for non-void functions) it carries a value back to the caller.
The basic shape:
A non-void function must have a return value; statement on every path through the body. "Every path" means every possible sequence of branches: the if branch, the else branch, every case of a switch. If the compiler can find a way for the function to reach the closing brace without returning, it flags it.
g++ -Wall flags this as warning: control reaches end of non-void function. The warning is right: if subtotal is 30.0, neither if runs, and the function falls off the end without returning anything. The behavior in that case is undefined, which is one of the easier kinds of bug to write and one of the harder to debug. The fix is to add a final return 0.0; for the "no discount" case:
Now every path returns a value, and the warning goes away.
For void functions, a return; statement (with no value) is optional. It's used to exit early when there's nothing more to do:
The first call (shipOrder(0)) hits the early return; and skips the rest of the body. The second call (shipOrder(3)) runs the full body. Early returns keep the success path code from being buried inside ever-deeper if blocks.
A function can have multiple return statements. There's nothing special about that. The first one that runs ends the function. Anything after it in the body doesn't execute.
Two returns, one path through each. Clean and easy to read.
Deeper return-type topics like auto return types, [[nodiscard]], and returning multiple values via std::tuple come up later. For now, the rules are: pick a return type, use return value; on every path for non-void, use void plus optional return; for action functions.
main is a FunctionThe main function in every program is itself a regular C++ function. Its header is:
The return type is int, the name is main, the parameter list is empty (a version takes arguments from the command line). The body runs when the program starts, and the int it returns is the program's exit code, where 0 conventionally means success and any non-zero value means some kind of failure.
The diagram shows the lifecycle. The operating system calls main to start the program. main calls whatever helper functions it needs, then returns an integer to the OS. The OS uses that integer to decide whether the program succeeded.
There's one tiny exception to the "every non-void function must return a value" rule, and it's main specifically. Omitting return 0; at the end of main makes the compiler add it implicitly. So these two programs are equivalent:
Don't rely on the implicit return in other functions, though. main is the only one with this special treatment. In other functions, write the return statement explicitly.
Function names follow the same rules as variable names: letters, digits, and underscores, no spaces, can't start with a digit. The convention in modern C++ is camelCase for functions: lowercase first letter, capitalized first letter of every subsequent word.
Good function names:
| Name | What it does |
|---|---|
calculateTotal | Computes the cart total |
displayCart | Prints the cart contents |
applyDiscount | Reduces the price by a discount |
getCustomerName | Returns the name of the current customer |
isInStock | Returns true if a product is available |
Names that work less well:
| Name | Problem |
|---|---|
doIt | Does what? |
calc | Calculate what? |
Total | Should be lowercase first letter; also, is this a function or a variable? |
function1 | The name says nothing about the job |
A good name describes the function's job in a way that lets a reader skip the body. Reading applyDiscount(cart) in main should give a guess about what it does without scrolling up. Names that need a comment to explain them are almost always too vague.
A useful convention: functions that return a bool often start with is, has, or should (like isInStock, hasShipped, shouldApplyDiscount). Calling code reads naturally: if (isInStock(product)) { ... }.
A small program uses several functions together. Each function has one job, and main strings them together into an order flow.
Read main first. It's six lines, and each one is a step in the order flow: greet the customer, show the cart, get the numbers, print the receipt. The program is understandable at that level without reading any of the function bodies.
For details, drop into each function. calculateSubtotal computes the math. getShippingFee returns a flat rate. printReceipt formats the output. Each function is small enough to fit on a screen, and each has a name that explains its job.
That layered reading (high-level flow first, details on demand) is the main reason functions exist. A main with all the logic inlined would be one big wall of code. Broken into named pieces, the same code becomes a series of steps a new reader can follow.
The mix of return types: printWelcome, displayCart, and printReceipt are void because they only print. calculateSubtotal and getShippingFee return double because their job is to hand back a number. The return type matches the job. That match is most of what picking a return type is about.
A few patterns cause issues for beginners. None of them are deep bugs, but each one is annoying enough to watch for.
Forgetting parentheses on a call. Without the parentheses, the function isn't called, just named.
The first line might or might not compile (depending on the function and surrounding code), but it definitely doesn't run the function. The parentheses are what make a call.
Forgetting the semicolon. A function call is a statement, so it needs a semicolon. A function definition is a block, so it doesn't (the closing brace is the end).
Using a `void` function's "return value". As shown above, double t = displayCart(); is a compile error. void means there's nothing to use.
Returning the wrong type. A function that returns double and uses return "hello"; will be rejected by the compiler (or do a confusing implicit conversion in some narrow cases). The type after return has to match (or be convertible to) the declared return type.
Defining a function inside another function. C++ does not allow defining a regular function inside the body of another function. This is wrong:
Functions live at the top level of the file (or inside a class, a topic for the OOP section). Lambdas, which look like inline functions, do exist and are covered later. They're a separate construct with different syntax.
10 quizzes