Functions in C++
A good function name describes the operation it performs, so that a call such as total_price(2.50, 4) explains its own purpose at the place where it is used.
A Shop Program Before and After Functions
Imagine a small shop program. It needs to work out the cost of an order and then print a receipt. A program can do both steps inline inside main, or it can package each meaningful operation into its own named function so that main reads like a summary of what the program does. This article follows that single idea through a sequence of design decisions, and at each step you will trace what the program is doing so you can see what each choice changes.
Here is the first function we will use throughout. It takes a unit price and a quantity and returns the total.
10total_priceis the name of the operation; the name alone tells a reader what the call computes- 2.50 and 4 are the arguments passed in by main
- return sends the computed value back to the caller
What do you think happens?
Before running it, what do you think total_price(2.50, 4) returns?
Reveal answer
10
The function multiplies the price 2.50 by the quantity 4, which gives 10. Notice that you could tell what the call was for from its name alone, before reading the body.
Why Names Carry the Explanation
A function is more than a way to avoid retyping code. The way you divide a program into functions decides how easy that program is to read, test, and change later. Two functions that do the same amount of work can be very different to live with. One reads like a clear list of steps, while the other is a long block where you must work out on your own what each part is for.
The names do most of the explaining. A reader who sees total_price(2.50, 4) already knows the purpose of the call without opening the function. Careful naming is what turns a set of packaged operations into something a reader can follow without reading every line. This is the first principle the source material gives for designing functions: package meaningful operations as carefully named functions.
A good function name describes the operation it performs, so that a call such as total_price(2.50, 4) explains its own purpose at the place where it is used.
Packaging also produces a natural hierarchy. The program sits at the top and calls a small number of meaningful functions. Each of those functions can be read on its own, which is why the design principles work together rather than separately.
Arguments and Return Values as Data Flow
When main calls a function with arguments, the values travel into the function through its parameters. The function does its work on those values and returns a result, which travels back to the caller at the place where the call was written. Tracing this path for total_price shows exactly how the result of 10 reaches main.
Read the diagram from the caller outward and back again. The two arguments enter the function through its parameters, price and quantity. The body multiplies them, and the return statement sends the single value 10 back to the place where total_price was called. Because the result is a value, it can be stored, compared, or printed, just like any other value your program produces.
Following a Call on the Call Stack
A call also changes which part of the program is currently active. While main is running and reaches the call to total_price, main pauses, total_price becomes the active piece of work, and when total_price returns, control goes back to main at the line after the call. Picturing the active functions as a stack, with the one currently running on top, makes that order easy to see.
In the diagram, main sits below and is paused while total_price is on top and doing the work. Once total_price returns its value, its frame is finished and main resumes at the line after the call, now holding 10 as the result. This is the mechanism that lets a short named call stand in for the whole computation.
Splitting a Function With Several Jobs
The second principle is that a function should perform a single logical operation. You can check this by trying to describe its job in one short phrase. If the honest description needs the word 'and' several times, the function is probably carrying more than one operation. A function that reads input, computes a total, and prints a receipt is doing three jobs, and each of those jobs is harder to check when they are tangled together.
#include <iostream>
void process_order() {
double price;
int quantity;
std::cin >> price;
std::cin >> quantity;
double total = price * quantity;
std::cout << "Total: " << total << "\n";
}One function holds input, computation, and output together, so a reader must work through all three to know what it does.
#include <iostream>
double total_price(double price, int quantity) {
return price * quantity;
}
void print_receipt(double total) {
std::cout << "Total: " << total << "\n";
}
int main() {
print_receipt(total_price(2.50, 4));
}total_price computes one value and print_receipt prints one value. Each can be understood, reused, and checked on its own.
In the split version, total_price computes a total and print_receipt prints one. The main function now reads as a summary: compute the total, then print the receipt. If the receipt format changes later, only print_receipt needs to change, and the calculation can still be tested by calling total_price directly.
Keeping Functions Short
Length is closely tied to the single-operation principle. A long function usually contains several logical operations stacked together, and each extra operation makes the whole body harder to hold in mind at once. A short function can be read in a single pass, so a reader can check what it does and move on.
Treat length as a warning sign rather than a rule. When a function grows long, ask whether it has taken on more than one job, and if so, look for the natural place to split it.
Overloading the Same Task on Different Types
The fourth principle concerns overloading. Use overloading when functions perform conceptually the same task on different types. The word 'conceptually' is the key. Both versions must be doing the same task, such as computing the area of a square, just with different types of numbers. Overloading does not justify sharing one name across unrelated tasks.
4
4- Both versions share the name area because they compute the same thing
- The call
area(2)passes an int, so the int version is the match - The call
area(2.0)passes a double, so the double version is the match
The caller does not need to remember a different name for each type. The compiler selects the version whose parameter type matches the argument it is given, so area(2) and area(2.0) both read naturally at the call site. This makes the code more flexible and generic, which is exactly the benefit the principle is pointing at.
The cmath Library at a Glance
Many programs need calculations beyond simple addition and multiplication, such as the absolute value of a difference, the square root of an area, the sine of an angle, or the logarithm of a measurement. C++ does not require you to write these from scratch. The standard <cmath> library supplies ready-made mathematical functions, and you call them by name with a value in parentheses. Each call takes an argument and produces a result that your program can store, compare, or print.
The <cmath> library provides standard mathematical functions such as sqrt, log, and sin, which accept float, double, and long double arguments.
| Family | Functions | What they compute |
|---|---|---|
| Absolute value and rounding | abs, ceil, floor | abs(x) gives the absolute value; ceil(x) and floor(x) give integers around x |
| Square root | sqrt | Square root of x; x must be non-negative |
| Trigonometric | sin, cos, tan | sin(x), cos(x), tan(x) for the value passed in |
| Inverse trigonometric | asin, acos, atan | Inverses of sin, cos, and tan |
| Hyperbolic | sinh, cosh, tanh | Hyperbolic sine, cosine, and tangent |
| Exponential and logarithmic | exp, log, log10 | exp(x) base e exponential; log(x) base e; log10(x) base 10 |
The standard math functions grouped into families. The source table lists sixteen functions in these groups.
Seeing the grouping makes the table easier to scan. First decide which family your problem belongs to, then look up the specific name. The trigonometric, hyperbolic, exponential, and logarithmic functions are all called the same way, by passing a value inside the parentheses. The reference table adds notes that matter for prediction: acos(x) returns a non-negative result, and asin(x) returns the result nearest to 0.
Rounding Negative Numbers With floor and ceil
The rounding functions are easy to misread. They do not round to the nearest integer in the everyday sense. ceil(x) returns the smallest integer that is greater than or equal to x, and floor(x) returns the largest integer that is less than or equal to x. Because of that definition, the direction of rounding depends on the sign of the input, and the number line makes this visible.
Predicting floor(-2.5) and ceil(-2.5)
What do floor(-2.5) and ceil(-2.5) return, using the definitions in the reference table?
- 1Read the floor definition
floor(x)returns the largest integer less than or equal to x. The integers at or below -2.5 are -3, -4, and so on, so the largest of them is -3. - 2Apply floor
floor(-2.5)returns -3, which lies to the left of -2.5 on the number line. - 3Read the ceil definition
ceil(x)returns the smallest integer greater than or equal to x. The integers at or above -2.5 are -2, -1, and so on, so the smallest of them is -2. - 4Apply ceil
ceil(-2.5)returns -2, which lies to the right of -2.5 on the number line. - 5Explain the flip
For a positive input such as 2.5, floor goes down to 2 and ceil goes up to 3. For a negative input, the same rule sends floor further negative, to -3, while ceil moves toward zero, to -2. The rule is fixed; the sign changes which way each function moves.
floor(-2.5) is -3 and ceil(-2.5) is -2.
What do you think happens?
What does floor(-2.5) return?
Reveal answer
-3
floor returns the largest integer less than or equal to -2.5. That integer is -3, not -2, because -2 is greater than -2.5.
Inputs Outside a Function's Domain
Each math function has a valid input range, sometimes called its domain. The reference table includes conditions on the input, and those conditions decide whether a call produces a meaningful value. The square root function sqrt(x) requires x to be non-negative, and log(x) requires x to be positive. A call outside those conditions is not a meaningful calculation, so the input should be checked before the call.
skipped- The condition x >= 0 checks the sqrt domain before the call
- The square root call runs only when the input is valid
- Because x is -4.0, the else branch runs
Mistakes Beginners Make
Common mistake Reading ceil and floor as ordinary rounding
What it looks likeExpecting ceil(-2.5) to be -3 because -2.5 is closer to -3 when rounded.
Why it failsceil(x)returns the smallest integer greater than or equal to x, andfloor(x)returns the largest integer less than or equal to x. These are defined by direction, not by distance.Do this insteadRead the definition and place the input on the number line. ceil(-2.5) is -2, because -2 is the smallest integer at or above -2.5.
Common mistake Calling sqrt or log without checking the input
What it looks likePassing a negative value to sqrt, or zero to log, and assuming a sensible answer comes back.
Why it failssqrt(x) requires a non-negative x and log(x) requires a positive x. Outside those conditions the call does not produce a meaningful value.
Do this insteadCheck the input condition before the call, as in an if statement that runs the call only for valid values.
Common mistake Putting several jobs in one function because it is convenient
What it looks likeA single function that reads input, computes a total, and prints the receipt.
Why it failsEach extra job makes the body harder to hold in mind, and the function is harder to reuse and test on its own.
Do this insteadDescribe the function in one short phrase. If you need several 'and' words, split it into named functions such as total_price and print_receipt.
Common mistake Using overloading to share a name across unrelated tasks
Why it failsOverloading is meant for conceptually the same task on different types. Giving unrelated operations the same name makes calls confusing to read.
Do this insteadOverload only when both versions do the same task, such as computing the area of a square from an int or a double.
Predicting Results Before Running
Use the reference descriptions, not guesswork, to predict each result. Work out the answer for each call from its definition and domain before checking it against a run of the program.
Predict the result of each call: abs(-4.5), ceil(-7.2), sqrt(0), and floor(3.9). For each one, state which definition you used.
Hints
abs(x)returns the absolute value, so a negative input comes back as its positive counterpart.ceil(x)returns the smallest integer greater than or equal to x;floor(x)returns the largest integer less than or equal to x.- sqrt requires a non-negative input, and 0 is allowed.
Rewrite a function that reads an order, computes its total, and prints a receipt as three named functions, then state in one short phrase what each one does. Check that the one-phrase description of each function contains no 'and'.
Hints
- Start from the split example with
total_priceandprint_receipt, then add a function for reading the order. - If a function needs the word 'and' in its one-phrase description, look for another split.
Key Takeaways
- Package meaningful operations as carefully named functions so that each call explains its own purpose at the place where it is used.
- A function should perform a single logical operation; check this by describing its job in one short phrase, and split the function when the description needs several 'and' words.
- Keep functions short, treat growing length as a warning sign, and use overloading only for conceptually the same task on different types.
- The <cmath> library provides ready-made math functions called by name, but several have domain conditions: sqrt needs a non-negative input, log needs a positive input, and the result of ceil and floor depends on the definitions of smallest or largest integer.
- Predict math results by reading the reference description: abs gives the positive counterpart, ceil rounds toward the smallest integer at or above x, and floor rounds toward the largest integer at or below x.