05

Module 5: Functions

Chapter 5 • Beginner

45 min

Functions

Functions are reusable blocks of code that perform specific tasks. They help organize code, reduce duplication, and make programs easier to understand and maintain.

What are Functions?

A function is a named block of code that:

  • Takes input (parameters)
  • Performs operations
  • Returns output (return value)

Benefits:

  • Code Reusability: Write once, use many times
  • Modularity: Break complex programs into smaller pieces
  • Maintainability: Fix bugs in one place
  • Readability: Descriptive names make code self-documenting
  • Testing: Test individual functions independently

Function Syntax

Basic Structure:

cpp.js
return_type function_name(parameter_list) {
    // function body
    return value;
}

Components:

  • Return Type: Data type of the value returned (use void if nothing returned)
  • Function Name: Identifier for the function
  • Parameters: Input values (can be empty)
  • Function Body: Code that executes
  • Return Statement: Value to return (optional for void)

Types of Functions

1. Functions with No Parameters and No Return

Example:

cpp.js
void greet() {
    cout << "Hello, World!" << endl;
}

int main() {
    greet();  // Call the function
    return 0;
}

2. Functions with Parameters

Example:

cpp.js
void greet(string name) {
    cout << "Hello, " << name << "!" << endl;
}

int main() {
    greet("Alice");
    greet("Bob");
    return 0;
}

3. Functions with Return Value

Example:

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

int main() {
    int result = add(5, 3);
    cout << "Sum: " << result << endl;
    return 0;
}

4. Functions with Multiple Parameters

Example:

cpp.js
int multiply(int a, int b, int c) {
    return a * b * c;
}

int main() {
    int product = multiply(2, 3, 4);
    cout << "Product: " << product << endl;
    return 0;
}

Function Declaration vs Definition

Function Declaration (Prototype)

Tells the compiler about a function before it's defined:

cpp.js
int add(int a, int b);  // Declaration

Function Definition

The actual implementation:

cpp.js
int add(int a, int b) {  // Definition
    return a + b;
}

Why use declarations?

  • Allows functions to be called before they're defined
  • Enables better code organization
  • Required when functions call each other

Example:

cpp.js
#include <iostream>
using namespace std;

// Function declarations
int add(int a, int b);
int multiply(int a, int b);

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

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

int multiply(int a, int b) {
    return a * b;
}

Parameter Passing

Pass by Value

Creates a copy of the argument. Changes don't affect original:

cpp.js
void increment(int x) {
    x++;  // Only changes local copy
    cout << "Inside function: " << x << endl;
}

int main() {
    int num = 5;
    increment(num);
    cout << "Outside function: " << num << endl;  // Still 5
    return 0;
}

Pass by Reference

Uses the original variable. Changes affect original:

cpp.js
void increment(int& x) {  // & means reference
    x++;  // Changes original
    cout << "Inside function: " << x << endl;
}

int main() {
    int num = 5;
    increment(num);
    cout << "Outside function: " << num << endl;  // Now 6
    return 0;
}

Pass by Pointer

Passes memory address. Changes affect original:

cpp.js
void increment(int* x) {  // * means pointer
    (*x)++;  // Dereference and increment
    cout << "Inside function: " << *x << endl;
}

int main() {
    int num = 5;
    increment(&num);  // Pass address
    cout << "Outside function: " << num << endl;  // Now 6
    return 0;
}

When to use each:

  • Pass by Value: Small data, don't need to modify original
  • Pass by Reference: Need to modify original, avoid copying large objects
  • Pass by Pointer: Optional parameters, arrays, C compatibility

Default Parameters

Provide default values for parameters:

cpp.js
int power(int base, int exponent = 2) {
    int result = 1;
    for (int i = 0; i < exponent; i++) {
        result *= base;
    }
    return result;
}

int main() {
    cout << power(3) << endl;      // Uses default: 3^2 = 9
    cout << power(3, 3) << endl;   // Override: 3^3 = 27
    return 0;
}

Rules:

  • Default parameters must be rightmost
  • Once a parameter has a default, all following must have defaults

Function Overloading

Multiple functions with same name but different parameters:

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

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

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

int main() {
    cout << add(5, 3) << endl;        // Calls int version
    cout << add(5.5, 3.2) << endl;    // Calls double version
    cout << add(1, 2, 3) << endl;     // Calls 3-parameter version
    return 0;
}

Rules:

  • Functions must differ in number or types of parameters
  • Return type alone is NOT enough for overloading

Recursion

Function that calls itself:

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

int main() {
    cout << factorial(5) << endl;  // 120
    return 0;
}

Important:

  • Must have a base case (stopping condition)
  • Each recursive call should move toward base case
  • Can be memory-intensive for deep recursion

Example: Fibonacci

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

Inline Functions

Small functions that are expanded inline (compiler suggestion):

cpp.js
inline int square(int x) {
    return x * x;
}

int main() {
    int result = square(5);  // May be expanded inline
    return 0;
}

Use when:

  • Function is small and called frequently
  • Want to avoid function call overhead
  • Note: Modern compilers often inline automatically

Lambda Functions (C++11)

Anonymous functions defined inline:

cpp.js
auto add = [](int a, int b) {
    return a + b;
};

int result = add(5, 3);

Syntax:

cpp.js
[capture](parameters) -> return_type { body }

Example:

cpp.js
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    vector<int> numbers = {1, 2, 3, 4, 5};
    
    // Lambda to double each number
    for_each(numbers.begin(), numbers.end(), 
             [](int& n) { n *= 2; });
    
    // Lambda to print
    for_each(numbers.begin(), numbers.end(),
             [](int n) { cout << n << " "; });
    
    return 0;
}

Common Function Patterns

Pattern 1: Validation Function

cpp.js
bool isValidAge(int age) {
    return age >= 0 && age <= 150;
}

Pattern 2: Calculation Function

cpp.js
double calculateArea(double radius) {
    const double PI = 3.14159;
    return PI * radius * radius;
}

Pattern 3: Utility Function

cpp.js
int max(int a, int b) {
    return (a > b) ? a : b;
}

Pattern 4: Multiple Return Values (using references)

cpp.js
void divide(int dividend, int divisor, int& quotient, int& remainder) {
    quotient = dividend / divisor;
    remainder = dividend % divisor;
}

Best Practices

  1. Use descriptive function names (calculateArea not calc)
  2. Keep functions small (single responsibility)
  3. Use const for parameters that shouldn't be modified
  4. Document with comments for complex functions
  5. Return early for error cases
  6. Avoid global variables - pass as parameters instead
  7. Use references to avoid copying large objects
  8. One function, one purpose

Common Mistakes

  • ❌ Forgetting return statement (for non-void functions)
  • ❌ Modifying parameters when you shouldn't (use const)
  • ❌ Functions too long (hard to understand)
  • ❌ Too many parameters (consider struct/class)
  • ❌ Infinite recursion (no base case)
  • ❌ Not handling edge cases

Next Module

In Module 6, we'll learn about Arrays and Strings - how to work with collections of data and text!

Hands-on Examples

Basic Functions

#include <iostream>
#include <string>
using namespace std;

// Function with no parameters
void greet() {
    cout << "Hello, World!" << endl;
}

// Function with parameters
void greetPerson(string name) {
    cout << "Hello, " << name << "!" << endl;
}

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

// Function with multiple parameters
int multiply(int a, int b, int c) {
    return a * b * c;
}

int main() {
    greet();
    greetPerson("Alice");
    greetPerson("Bob");
    
    int sum = add(10, 20);
    cout << "Sum: " << sum << endl;
    
    int product = multiply(2, 3, 4);
    cout << "Product: " << product << endl;
    
    return 0;
}

Functions can have no parameters, one parameter, multiple parameters, or return values. Functions help organize code and make it reusable.