Functions

Functions

1. What is a Function?

A function is a block of code that performs a specific task.

#include <iostream>

// Function definition
void sayHello() {
    std::cout << "Hello!" << std::endl;
}

int main() {
    sayHello();  // Function call
    sayHello();
    return 0;
}

Function Structure

return_type function_name(parameters) {
    // Function body
    return value;  // Can be omitted for void
}

2. Function Declaration and Definition

Declaration (Prototype)

#include <iostream>

// Function declaration (prototype)
int add(int a, int b);

int main() {
    std::cout << add(3, 5) << std::endl;  // 8
    return 0;
}

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

Declaration in Header Files

// math_utils.h
#ifndef MATH_UTILS_H
#define MATH_UTILS_H

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

#endif
// math_utils.cpp
#include "math_utils.h"

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

int multiply(int a, int b) {
    return a * b;
}
// main.cpp
#include <iostream>
#include "math_utils.h"

int main() {
    std::cout << add(3, 5) << std::endl;
    std::cout << multiply(3, 5) << std::endl;
    return 0;
}

3. Parameter Passing Methods

Pass by Value

#include <iostream>

void increment(int n) {  // n is a copy
    n++;
    std::cout << "Inside function: " << n << std::endl;
}

int main() {
    int x = 10;
    increment(x);
    std::cout << "After function: " << x << std::endl;  // 10 (unchanged)
    return 0;
}

Pass by Reference

#include <iostream>

void increment(int& n) {  // n is a reference to original
    n++;
    std::cout << "Inside function: " << n << std::endl;
}

int main() {
    int x = 10;
    increment(x);
    std::cout << "After function: " << x << std::endl;  // 11 (changed)
    return 0;
}

Pass by Pointer

#include <iostream>

void increment(int* n) {  // n is an address
    (*n)++;
    std::cout << "Inside function: " << *n << std::endl;
}

int main() {
    int x = 10;
    increment(&x);  // Pass address
    std::cout << "After function: " << x << std::endl;  // 11
    return 0;
}

const Reference (Read-only)

#include <iostream>
#include <string>

// Read without copy (efficient)
void printLength(const std::string& str) {
    std::cout << "Length: " << str.length() << std::endl;
    // str[0] = 'x';  // Error! Cannot modify const
}

int main() {
    std::string name = "Hello";
    printLength(name);
    return 0;
}

When to Use Which Method?

Situation Recommended Method
Small types (int, double) for reading Pass by value
Large types for reading const T&
Need to modify T&
Need to allow nullptr T*

4. Return Values

Single Value Return

int square(int n) {
    return n * n;
}

Reference Return (Use with Caution)

#include <iostream>

int& getElement(int arr[], int index) {
    return arr[index];  // Return reference to array element
}

int main() {
    int arr[] = {1, 2, 3, 4, 5};

    getElement(arr, 2) = 100;  // arr[2] = 100
    std::cout << arr[2] << std::endl;  // 100

    return 0;
}

Multiple Value Return (C++17 Structured Bindings)

#include <iostream>
#include <tuple>

std::tuple<int, int, int> getStats(int arr[], int size) {
    int sum = 0, min = arr[0], max = arr[0];
    for (int i = 0; i < size; i++) {
        sum += arr[i];
        if (arr[i] < min) min = arr[i];
        if (arr[i] > max) max = arr[i];
    }
    return {sum, min, max};
}

int main() {
    int arr[] = {5, 2, 8, 1, 9};

    // C++17 structured bindings
    auto [sum, min, max] = getStats(arr, 5);
    std::cout << "Sum: " << sum << ", Min: " << min << ", Max: " << max << std::endl;

    return 0;
}

5. Default Parameters

#include <iostream>

void greet(std::string name = "Guest", int times = 1) {
    for (int i = 0; i < times; i++) {
        std::cout << "Hello, " << name << "!" << std::endl;
    }
}

int main() {
    greet();                // Hello, Guest!
    greet("Alice");         // Hello, Alice!
    greet("Bob", 3);        // Hello, Bob! (3 times)
    return 0;
}

Rules

// Default values must be from right to left
void func(int a, int b = 10, int c = 20);  // OK
// void func(int a = 5, int b, int c = 20);  // Error!

// Only in declaration or definition, not both
void func(int a, int b = 10);  // Default in declaration
void func(int a, int b) { }    // No default in definition

6. Function Overloading

You can define multiple functions with the same name but different parameters.

#include <iostream>

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

// Floating-point addition
double add(double a, double b) {
    return a + b;
}

// Three-number addition
int add(int a, int b, int c) {
    return a + b + c;
}

int main() {
    std::cout << add(3, 5) << std::endl;        // int version: 8
    std::cout << add(3.5, 2.5) << std::endl;    // double version: 6.0
    std::cout << add(1, 2, 3) << std::endl;     // three-number version: 6
    return 0;
}

Overloading Rules

// Different parameter types: OK
void print(int n);
void print(double n);
void print(std::string s);

// Different number of parameters: OK
void print(int a);
void print(int a, int b);

// Different return type only: NOT allowed!
// int func(int a);
// double func(int a);  // Error!

7. inline Functions

Reduces call overhead for short functions.

#include <iostream>

inline int square(int n) {
    return n * n;
}

int main() {
    std::cout << square(5) << std::endl;  // Compiler may substitute with 25
    return 0;
}

When to Use

  • When function body is short (1-2 lines)
  • Frequently called functions
  • Compiler makes final decision (inline is a hint)

8. Recursive Functions

A function that calls itself.

Factorial

#include <iostream>

int factorial(int n) {
    if (n <= 1) return 1;       // Base case
    return n * factorial(n - 1); // Recursive call
}

int main() {
    std::cout << "5! = " << factorial(5) << std::endl;  // 120
    return 0;
}

Fibonacci

#include <iostream>

int fibonacci(int n) {
    if (n <= 1) return n;
    return fibonacci(n - 1) + fibonacci(n - 2);
}

int main() {
    for (int i = 0; i < 10; i++) {
        std::cout << fibonacci(i) << " ";
    }
    std::cout << std::endl;  // 0 1 1 2 3 5 8 13 21 34
    return 0;
}

Recursion vs Iteration

// Iterative version (efficient)
int factorialLoop(int n) {
    int result = 1;
    for (int i = 2; i <= n; i++) {
        result *= i;
    }
    return result;
}

9. Function Pointers

Functions can be stored and passed like variables.

#include <iostream>

int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }
int multiply(int a, int b) { return a * b; }

int main() {
    // Function pointer declaration
    int (*operation)(int, int);

    operation = add;
    std::cout << "Add: " << operation(5, 3) << std::endl;  // 8

    operation = subtract;
    std::cout << "Subtract: " << operation(5, 3) << std::endl;  // 2

    operation = multiply;
    std::cout << "Multiply: " << operation(5, 3) << std::endl;  // 15

    return 0;
}

Callback Functions

#include <iostream>

void processArray(int arr[], int size, int (*func)(int)) {
    for (int i = 0; i < size; i++) {
        arr[i] = func(arr[i]);
    }
}

int doubleIt(int n) { return n * 2; }
int squareIt(int n) { return n * n; }

int main() {
    int arr[] = {1, 2, 3, 4, 5};

    processArray(arr, 5, doubleIt);
    for (int n : arr) std::cout << n << " ";  // 2 4 6 8 10
    std::cout << std::endl;

    processArray(arr, 5, squareIt);
    for (int n : arr) std::cout << n << " ";  // 4 16 36 64 100
    std::cout << std::endl;

    return 0;
}

10. Lambda Expressions - Preview

From C++11, you can create anonymous functions.

#include <iostream>

int main() {
    // Basic lambda
    auto add = [](int a, int b) {
        return a + b;
    };

    std::cout << add(3, 5) << std::endl;  // 8

    // Capture
    int multiplier = 10;
    auto multiply = [multiplier](int n) {
        return n * multiplier;
    };

    std::cout << multiply(5) << std::endl;  // 50

    return 0;
}

11. main Function Parameters

#include <iostream>

int main(int argc, char* argv[]) {
    std::cout << "Argument count: " << argc << std::endl;

    for (int i = 0; i < argc; i++) {
        std::cout << "argv[" << i << "]: " << argv[i] << std::endl;
    }

    return 0;
}

Execution:

./program hello world

Output:

Argument count: 3
argv[0]: ./program
argv[1]: hello
argv[2]: world

12. Practice Examples

Greatest Common Divisor (Euclidean Algorithm)

#include <iostream>

int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}

int main() {
    std::cout << "gcd(48, 18) = " << gcd(48, 18) << std::endl;  // 6
    std::cout << "gcd(56, 98) = " << gcd(56, 98) << std::endl;  // 14
    return 0;
}

Swap Two Values

#include <iostream>

void swap(int& a, int& b) {
    int temp = a;
    a = b;
    b = temp;
}

int main() {
    int x = 5, y = 10;
    std::cout << "Before: x=" << x << ", y=" << y << std::endl;

    swap(x, y);
    std::cout << "After: x=" << x << ", y=" << y << std::endl;

    return 0;
}

13. Summary

Concept Description
Function declaration Function signature (prototype)
Function definition Function body
Pass by value Copy is passed, original unchanged
Pass by reference Original is passed, can be modified
const reference Read-only original passing
Default parameters Arguments that can be omitted
Overloading Same name, different parameters
inline Code insertion instead of function call
Recursion Function calling itself

Next Step

Let's learn about arrays and strings in 05_Arrays_and_Strings.md!

to navigate between lessons