Concepts / Functions in C++

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.

  • Programming

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.

cpp
Output
10
Notes on the code
  • total_price is 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?

  • 4

  • 6.5

  • 10

  • 2.50

Reveal answer
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.

Key point

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.

maincall site2.50argument for pricetotal_price bodyprice times quantity10return value4argument for quantitypassespassesbound to pricebound to quantitycomputes and returnsresult comes back
When main calls total_price with 2.50 and 4, where do those values go inside the function, and how does the result come back to 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.

mainpaused at the calltotal_priceactive, computing 10callreturn 10
When main calls total_price, which function is active on top of the stack, and what happens to main when total_price returns?

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.

process_orderreads, computes, and printsread_ordergets price and quantitytotal_pricecomputes the totalprint_receiptprints one linefeedsfeeds
How does one function that reads, computes, and prints differ in structure from three small named functions that each do one step?
Before
#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.

After
#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.

Best practice

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.

cpp
Output
4
4
Notes on the code
  • 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.

Definition

The <cmath> library provides standard mathematical functions such as sqrt, log, and sin, which accept float, double, and long double arguments.

FamilyFunctionsWhat they compute
Absolute value and roundingabs, ceil, floorabs(x) gives the absolute value; ceil(x) and floor(x) give integers around x
Square rootsqrtSquare root of x; x must be non-negative
Trigonometricsin, cos, tansin(x), cos(x), tan(x) for the value passed in
Inverse trigonometricasin, acos, atanInverses of sin, cos, and tan
Hyperbolicsinh, cosh, tanhHyperbolic sine, cosine, and tangent
Exponential and logarithmicexp, log, log10exp(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.

-3floor of -2.5-2.5input-2ceil of -2.52floor of 2.52.5input3ceil of 2.5
Where do floor(-2.5) and ceil(-2.5) land on the number line, and why does the direction of rounding flip for negative numbers?
Worked example

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?

  1. 1
    Read 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.

  2. 2
    Apply floor

    floor(-2.5) returns -3, which lies to the left of -2.5 on the number line.

  3. 3
    Read 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.

  4. 4
    Apply ceil

    ceil(-2.5) returns -2, which lies to the right of -2.5 on the number line.

  5. 5
    Explain 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.

Result

floor(-2.5) is -3 and ceil(-2.5) is -2.

What do you think happens?

What does floor(-2.5) return?

  • -2

  • -3

  • -2.5

  • -2.0

Reveal answer
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.

cpp
Output
skipped
Notes on the code
  • 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 like

    Expecting ceil(-2.5) to be -3 because -2.5 is closer to -3 when rounded.

    Why it fails

    ceil(x) returns the smallest integer greater than or equal to x, and floor(x) returns the largest integer less than or equal to x. These are defined by direction, not by distance.

    Do this instead

    Read 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 like

    Passing a negative value to sqrt, or zero to log, and assuming a sensible answer comes back.

    Why it fails

    sqrt(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 instead

    Check 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 like

    A single function that reads input, computes a total, and prints the receipt.

    Why it fails

    Each extra job makes the body harder to hold in mind, and the function is harder to reuse and test on its own.

    Do this instead

    Describe 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 fails

    Overloading is meant for conceptually the same task on different types. Giving unrelated operations the same name makes calls confusing to read.

    Do this instead

    Overload 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.

EASY

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.
MEDIUM

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_price and print_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.

Related concepts

More concepts starting with F