Functions

Functions let you package logic into named, reusable units — this topic covers declaring, defining, calling, and returning from them, from the simplest void function to composing whole programs out of focused pieces.

A function is a named unit of code that performs a specific task. Functions allow a C++ program to divide a large problem into smaller, manageable pieces. Instead of writing the same logic repeatedly, you can place it inside a function and call that function whenever needed. Functions are fundamental to C++ because they provide code reuse, modularity, better organization, easier testing, easier debugging, separation of responsibilities, and abstraction of implementation details.

1. Basic Function Syntax

A C++ program is typically split into many small functions, each responsible for one task. The program as a whole calls into these functions to get its work done.

A program delegates individual tasks to individual functions instead of doing everything in one place.

A simple function looks like this:

The general shape every function follows: a return type, a name, a parameter list, and a body in braces.

This function returns void, is named greet, takes no parameters, and prints a message. It can be called using greet();.

C++ Program
                         |
          +--------------+--------------+
          |              |              |
       Function A     Function B     Function C
          |              |              |
        Task 1         Task 2         Task 3
return_type function_name(parameters)
{
    // function body
}
void greet()
{
    std::cout << "Hello!";
}

2. Calling a Function

Defining a function does not execute it. The function executes only when it is called.

main() calls greet(), which runs its body and prints "Hello", and then control returns to main().

The execution flow: control moves from main into greet, greet does its work, and then control returns.

#include <iostream>
void greet()
{
    std::cout << "Hello\n";
}
int main()
{
    greet();
    return 0;
}
main()
  |
  v
greet()
  |
  v
print "Hello"
  |
  v
return to main()

3. Function Declaration

A function declaration tells the compiler that a function exists. This is also called a function prototype. It provides information such as the function name, return type, and parameter types.

A bare declaration — no body — just announces that a function named greet exists, returns void, and takes no parameters.

This tells the compiler: function name -> add, return type -> int, parameter 1 -> int, parameter 2 -> int. The actual implementation can appear later.

The matching definition, which can appear elsewhere in the file (or in another file) after the declaration.

void greet();
int add(int a, int b);
int add(int a, int b)
{
    return a + b;
}

4. Function Definition

A function definition provides the actual implementation.

The definition contains the function body — the code that actually runs when the function is called.

A useful distinction: a declaration says "this function exists," a definition says "this is what the function does."

int add(int a, int b)
{
    return a + b;
}

5. Declaration Before Use

If a function is defined after main(), declare it before main() so the compiler knows about it when it sees the call.

The declaration on line 2 lets main() call add() even though add()'s full definition appears after main().

Without the declaration, the compiler may encounter the call before it knows the function's declaration.

#include <iostream>
int add(int a, int b);
int main()
{
    int result = add(10, 20);
    std::cout << result;
    return 0;
}
int add(int a, int b)
{
    return a + b;
}

6. Function Parameters

Parameters allow data to be passed into a function.

Calling greet("Rina"); passes the string "Rina" into the function.

name is the parameter — it is part of the function's declaration/definition.

"Rina" is the argument — the value supplied at the call site.

void greet(std::string name)
{
    std::cout << "Hello " << name;
}

7. Multiple Parameters

A function can accept multiple parameters.

Calling int result = add(10, 20); supplies both parameters at once.

Each argument lines up with the parameter in the same position: 10 becomes a, 20 becomes b.

int add(int a, int b)
{
    return a + b;
}
add(10, 20)
   |    |
   |    +----> b
   +---------> a
        |
        v
      a + b
        |
        v
       30

8. Return Type

The return type specifies the type of value a function returns.

The function returns an int. The returned value can be stored (int value = getNumber();) or used directly (std::cout << getNumber();).

int getNumber()
{
    return 42;
}

9. return Statement

The return statement exits the function and optionally provides a value.

Calling int result = square(5); produces result = 25.

The function computes a value and hands it back to whoever called it.

int square(int x)
{
    return x * x;
}
square(5)
   |
   v
x * x
   |
   v
return 25
   |
   v
caller receives 25

10. void Functions

A function that does not return a value can use void.

This function performs an action (printing) but hands nothing back to the caller.

A void function can use return; to exit early.

If value is negative, the bare return; statement exits the function immediately, skipping the rest of the body.

void printMessage()
{
    std::cout << "Hello\n";
}
void check(int value)
{
    if (value < 0)
    {
        return;
    }
    std::cout << "Valid value";
}

11. Function Returning bool

Functions can return Boolean values.

isEven evaluates a condition and returns the resulting true or false.

A function's return value can directly participate in another expression or condition, such as the condition of an if statement.

bool isEven(int number)
{
    return number % 2 == 0;
}
if (isEven(10))
{
    std::cout << "Even";
}

12. Function Returning double

The return type can be any appropriate type, including double.

Called as double average = calculateAverage(10.0, 20.0);, this returns a floating-point result.

double calculateAverage(double a, double b)
{
    return (a + b) / 2.0;
}

13. Function Returning a Class or Other Object

C++ functions can return objects and values of user-defined types, not just built-in types like int or double.

createPoint's return type is Point. Usage: Point p = createPoint(10, 20); constructs a Point and stores it in p.

struct Point
{
    int x;
    int y;
};
Point createPoint(int x, int y)
{
    return {x, y};
}

14. Pass-by-Value

By default, function parameters are passed by value.

Calling this with an int number does not change number in the caller.

This prints 10, not 100.

Why? The caller's number = 10 is copied into the function's own x = 10; the function then modifies x to 100, but that modifies its own parameter object, not the caller's number.

void change(int x)
{
    x = 100;
}
int number = 10;
change(number);
std::cout << number;

15. Passing Multiple Values by Value

Same by-value behavior applies with multiple parameters.

The function receives values corresponding to x and y. The function can modify its parameter variables (a and b) without changing the caller's original variables (x and y).

int add(int a, int b)
{
    return a + b;
}
int x = 10;
int y = 20;
int result = add(x, y);

16. Function Parameters and Types

The argument must be compatible with the parameter type according to C++'s normal type-checking and conversion rules.

Calling calculate(10); works even though 10 is an int literal — the integer 10 can be converted to double. Function calls therefore interact with C++'s type conversion rules.

double calculate(double value)
{
    return value * 2;
}

17. No Parameters

A function can accept no arguments.

Called as int value = getDefaultValue();, with nothing passed in.

In modern C++, an empty parameter list means the function accepts no parameters: void process();

int getDefaultValue()
{
    return 100;
}

18. Function with No Return Value

Called as display();, there is no returned value to store.

void display()
{
    std::cout << "Displaying data\n";
}

19. Multiple return Statements

A function can contain multiple return statements.

The first return is executed for negative values. Otherwise, the second return executes.

int absoluteValue(int value)
{
    if (value < 0)
    {
        return -value;
    }
    return value;
}

20. Early Return

An early return can simplify conditional logic.

Returning immediately when a precondition fails avoids unnecessary nesting for the rest of the function.

bool process(int value)
{
    if (value < 0)
    {
        return false;
    }
    // Continue processing
    return true;
}

21. Function Call Flow

main() calls multiply(4, 5), which computes and returns a value that main() stores in result.

A function call transfers control to the function and eventually returns control to the caller.

int multiply(int a, int b)
{
    return a * b;
}
int main()
{
    int result = multiply(4, 5);
    return 0;
}
main()
  |
  | multiply(4, 5)
  v
multiply()
  |
  | a = 4
  | b = 5
  |
  | return a * b
  v
20
  |
  v
main()
  |
  v
result = 20

22. Functions Calling Other Functions

calculate() calls square() as part of computing its own result.

Calling int result = calculate(5); produces 35. This allows larger operations to be composed from smaller functions.

int square(int x)
{
    return x * x;
}
int calculate(int x)
{
    return square(x) + 10;
}
calculate(5)
     |
     v
square(5)
     |
     v
  25 + 10
     |
     v
    35

23. main() Is a Function

The program entry point in a hosted C++ program is main. main is itself a function with a special role defined by the C++ language.

The simplest possible main — it does nothing and returns 0 to signal success.

main can call other functions, just like any other function can.

int main()
{
    return 0;
}
int add(int a, int b)
{
    return a + b;
}
int main()
{
    int result = add(5, 10);
    return 0;
}

24. Function Scope

Variables declared inside a function generally have local scope.

value cannot be directly accessed from outside that scope. The variable value belongs to the scope where it was declared.

void calculate()
{
    int value = 10;
    std::cout << value;
}

25. Local Variables in Functions

Each invocation of a function has its own execution context for its ordinary local variables.

Calling test(); test(); normally produces 1 1 — each call has its own local count, freshly initialized to 0 each time.

void test()
{
    int count = 0;
    ++count;
    std::cout << count << '\n';
}

26. Function Declaration and Definition Example

A common multi-part structure separates a function's declaration from its definition.

The declaration int multiply(int, int); provides the function's interface; the definition provides its implementation.

Parameter names are optional in a declaration but are normally included when they improve readability.

#include <iostream>
int multiply(int, int);
int main()
{
    std::cout << multiply(4, 5);
    return 0;
}
int multiply(int a, int b)
{
    return a * b;
}

27. Function Signatures

A function's interface includes its name and parameter information.

These represent different functions because their parameter types differ. This becomes particularly important with function overloading.

int add(int a, int b);
double add(double a, double b);

28. Functions Should Have Focused Responsibilities

A good function usually performs a clear task. Instead of one large function that does everything:

A single oversized function mixes many unrelated responsibilities together, which makes it harder to read and test.

It can be divided conceptually into focused functions. This improves readability and makes individual pieces easier to test.

void processEverything()
{
    // input, validation, calculation, formatting, output, many unrelated operations
}
readInput();
validateInput();
calculateResult();
displayResult();

29. Function Naming

Function names should describe an action or operation.

Descriptive names make a call site self-explanatory without needing to look up the implementation.

Boolean-returning functions often use names that read naturally as conditions: isValid(); isEmpty(); hasData(); canExecute();

Good:  calculateTotal();  validateInput();  readTemperature();  printResult();  isValid();
Less descriptive:  doIt();  thing();  process();  func1();

30. Best Practices

1. Keep functions focused — one function should ideally have a clear responsibility.

2. Use meaningful names — calculateAverage() is clearer than calc().

3. Keep parameters understandable — avoid unnecessarily large parameter lists.

4. Prefer clear return behavior — make it easy for the caller to understand what a function returns.

5. Declare functions before they are used when necessary — use declarations/prototypes when the definition appears later.

6. Avoid unnecessary global state — functions are easier to understand when their behavior depends on clearly defined inputs and outputs.

7. Use early returns when they improve clarity — this can be clearer than deeply nested logic.

8. Call a function with the exact number and type of arguments its declaration expects, and give every non-void function a return value along every reachable path, so the caller always gets a well-defined result.

9. Keep parameters and arguments straight in your own head when reading a call site: in int add(int a, int b), a and b are parameters; in add(10, 20), 10 and 20 are the arguments supplied for them.

if (!isValid)
{
    return false;
}

31. Complete Example

This program declares three functions up front, then defines them after main. Each function performs one focused operation: summing, printing, and checking evenness.

main() coordinates the work by calling each focused function in turn.

#include <iostream>
int calculateSum(int a, int b);
bool isEven(int number);
void printResult(int value);
int main()
{
    int first = 10;
    int second = 20;
    int sum = calculateSum(first, second);
    printResult(sum);
    if (isEven(sum))
    {
        std::cout << "The sum is even\n";
    }
    return 0;
}
int calculateSum(int a, int b)
{
    return a + b;
}
bool isEven(int number)
{
    return number % 2 == 0;
}
void printResult(int value)
{
    std::cout << "Result: " << value << '\n';
}
main()
                      |
             +--------+--------+
             |        |        |
             v        v        v
       calculateSum  printResult  isEven
             |                   |
             v                   v
           result             true/false

32. Function Mental Model

Think of a function as a reusable processing unit that takes arguments in and produces a return value out.

A value-returning function's mental model: arguments flow in, processing happens, a return value flows out.

For a void function, arguments still flow in and processing still happens, but control simply returns to the caller with no value.

Arguments
                 |
                 v
        +----------------+
        |    Function    |
        |                |
        |   Processing   |
        |                |
        +----------------+
                 |
                 v
             Return value
Arguments
                 |
                 v
        +----------------+
        |    Function    |
        |                |
        |   Processing   |
        |                |
        +----------------+
                 |
                 v
              Caller

33. Quick Reference

A condensed glossary of every core function-related term covered in this topic.

What is the difference between a function declaration and a function definition?

A declaration (prototype) only tells the compiler that a function exists, giving its name, return type, and parameter types, e.g. int add(int a, int b);. A definition provides the actual implementation, including the function body, e.g. int add(int a, int b) { return a + b; }. A declaration says a function exists; a definition says what it does.

In the call add(10, 20) for int add(int a, int b), which are the parameters and which are the arguments?

a and b are the parameters — they are the named inputs listed in the function's declaration/definition. 10 and 20 are the arguments — the actual values supplied at the call site.

Given void change(int x) { x = 100; } called as int number = 10; change(number); std::cout << number;, what gets printed and why?

It prints 10. Parameters are passed by value by default, so number's value (10) is copied into change's local parameter x. Modifying x inside the function only changes that local copy — it has no effect on the caller's number.

Why must you declare a function before main() if its definition appears after main() in the file?

The compiler processes a file from top to bottom and needs to know a function's name, return type, and parameter types before it sees a call to that function. Without an earlier declaration, the compiler encounters the call in main() before it has any information about the function, which is an error.

What does an early return like if (value < 0) { return false; } // continue processing accomplish, and why is it considered good practice?

It exits the function immediately when a precondition (value < 0) is met, so the rest of the function body doesn't need to be wrapped in an else block or extra nesting. This keeps the main logic of the function at a shallower, easier-to-read indentation level.

What does this program print, and what is the order of calls?

It prints 35. main() calls calculate(5), which calls square(5) (returning 25), then adds 10 to get 35, which calculate returns to main(), which prints it.

What does this program print? Why doesn't count keep increasing across calls?

It prints 1 1 1. Each call to test() creates its own fresh local variable count, initialized to 0, so incrementing it always produces 1 within that call. The variable does not persist between separate invocations.

Write a function bool isPositive(int number) that returns true if the number is greater than zero and false otherwise, then write a main() that declares isPositive before use, calls it with the value -5, and prints "Positive" or "Not positive" based on the result.

The program declares isPositive before main() so the compiler knows its interface, calls it with -5, and since -5 is not greater than 0, isPositive returns false, so the program prints "Not positive".

Write three small, focused functions — int readValue() that returns a fixed value of 8, int doubleValue(int value) that returns value doubled, and void printValue(int value) that prints the value — then write a main() that reads a value, doubles it, and prints the result, demonstrating functions calling other functions.

main() calls readValue() to get 8, passes it to doubleValue() to get 16, then passes 16 to printValue(), which prints "Value: 16". Each function has one focused responsibility, and main() composes them together.

Concept             | Meaning
---------------------+---------------------------------------------
Function             | Named reusable unit of code
Declaration          | Tells the compiler a function exists
Definition           | Provides the implementation
Call                 | Executes a function
Parameter            | Named input in function declaration/definition
Argument             | Value supplied during a call
Return type          | Type of value returned
void                 | No returned value
return               | Exits function and optionally returns a value
Pass-by-value        | Function receives a value for its parameter
Local variable       | Variable whose scope is local to its declaration
Function prototype   | Function declaration describing its interface
#include <iostream>
int square(int x)
{
    return x * x;
}
int calculate(int x)
{
    return square(x) + 10;
}
int main()
{
    std::cout << calculate(5);
    return 0;
}
#include <iostream>
void test()
{
    int count = 0;
    ++count;
    std::cout << count << ' ';
}
int main()
{
    test();
    test();
    test();
    return 0;
}