C++ Functions Modular Programming
Code Reusability

C++ Functions: Complete Guide with Examples

Master C++ functions: function declaration, definition, parameters, return values, call by value/reference, passing arrays, recursion, and best practices for modular programming.

Function Basics

Declaration & Definition

Parameters

Value vs Reference

Arrays

1D & 2D Passing

Recursion

Self-calling Functions

C++ Tutorial · Functions

Functions structure code into reusable units. Master prototypes, scope, parameter passing, overloading, and inline hints—the building blocks before recursion and OOP methods.

What you will learn

  • Declare and define functions with correct signatures
  • Pass by value, reference, and const reference
  • Overload functions by parameter lists
  • Use default arguments and inline appropriately
  • Separate declaration (.h) from implementation (.cpp)

Why this topic matters

Every interview uses functions. Parameter passing and overloading distinguish candidates who understand memory and APIs.

Key terms & indexing

C++ functions function overloading C++ pass by reference C++ inline function C++

Introduction to Functions

Functions are fundamental building blocks in C++ that allow you to organize code into reusable, modular units. They enable code reuse, improve readability, and make debugging easier.

Why Use Functions?
  • Code Reusability
  • Modular Programming
  • Improved Readability
  • Easier Debugging
  • Code Organization
  • Team Collaboration
Function Components
  • Return Type: Data type returned
  • Function Name: Identifier
  • Parameters: Input values
  • Function Body: Implementation
  • Prototype: Declaration
Function Execution Flow
1
Function Call: Call function with arguments
2
Parameter Passing: Arguments copied to parameters
3
Function Execution: Execute function body
4
Return Value: Return result to caller
5
Resume Execution: Continue after function call

C++ Functions Overview

The following table compares different function types and parameter passing methods in C++:

Function Type Syntax Example When to Use Characteristics
Basic Function int add(int a, int b) { } Simple operations, calculations Returns value, has parameters
Void Function void printMsg() { } Actions without return value No return value, may have parameters
Call by Value void func(int x) { } Don't modify original values Creates copy, original unchanged
Call by Reference void func(int &x) { } Modify original values Works on original, no copy
Array Parameter void func(int arr[], int size) { } Process array elements Passes address, modifies original
Recursive Function int factorial(int n) { } Problems with recursive nature Calls itself, base case needed

1. Function Basics: Declaration & Definition

A function consists of a declaration (prototype) and definition. The declaration specifies the function's interface, while the definition provides the implementation.

Function Components
// FUNCTION DECLARATION (Prototype)
return_type function_name(parameter_list);

// FUNCTION DEFINITION
return_type function_name(parameter_list) {
    // Function body
    // Statements
    return value; // if return_type is not void
}

Examples

1. void function
void greet() {
    cout << "Hello";
}
2. Return int
int add(int a, int b) {
    return a + b;
}
3. Call function
int sum = add(3, 5);  // 8
4. Function prototype
int multiply(int, int);
// ... later definition
5. Default args
void print(int n = 10);
6. inline hint
inline int sq(int x) { return x*x; }
Function Naming Conventions:
  • Use descriptive names (calculateAverage, isValidInput)
  • CamelCase or snake_case (calculateAverage or calculate_average)
  • Start with verb for actions (print, calculate, validate)
  • Boolean functions start with "is", "has", "can" (isValid, hasPermission)
Common Mistakes:
  • Missing return statement in non-void function
  • Function prototype mismatch with definition
  • Forgetting semicolon after prototype
  • Redeclaring parameters in function body

2. Parameter Passing: Call by Value vs Reference

C++ supports two main ways to pass parameters: call by value (creates copy) and call by reference (works on original).

Parameter Passing Syntax
// CALL BY VALUE (creates copy)
void func(int x) {
    x = 100; // Changes local copy only
}

// CALL BY REFERENCE (works on original)
void func(int &x) {
    x = 100; // Changes original variable
}

// CALL BY CONST REFERENCE (read-only, efficient)
void func(const int &x) {
    // Can read x but not modify
}

Examples

1. Pass by value
void f(int x) { x = 10; }
int a = 5; f(a);  // a still 5
2. Pass by reference
void f(int& x) { x = 10; }
int a = 5; f(a);  // a is 10
3. const reference
void f(const int& x) {
    cout << x;  // read-only
}
4. Pass by pointer
void f(int* p) { *p = 10; }
f(&a);
5. Swap with ref
void swap(int& a, int& b) {
    int t = a; a = b; b = t;
}
6. Return reference
int& maxRef(int& a, int& b) {
    return (a > b) ? a : b;
}
Call by Value
  • Creates copy of argument
  • Original unchanged
  • Good for small data types
  • Inefficient for large objects
  • Use when don't need to modify
Call by Reference
  • Works on original data
  • Can modify original
  • Efficient for large objects
  • Use & symbol in parameters
  • Use when need to modify
Const Reference
  • Read-only access
  • Efficient, no copying
  • Cannot modify data
  • Best for large read-only data
  • Use const keyword

3. Passing Arrays to Functions: 1D & 2D Arrays

Arrays are always passed by reference to functions. For 1D arrays, you need to pass the size separately. For 2D arrays, you must specify column size.

Array Parameter Syntax
// 1D ARRAY PARAMETER
void processArray(int arr[], int size) {
    // arr is pointer to first element
}

// 2D ARRAY PARAMETER - MUST SPECIFY COLUMNS
void processMatrix(int matrix[][COLS], int rows) {
    // Column size must be specified
}

// USING POINTER NOTATION
void processArray(int *arr, int size) {
    // Same as arr[]
}

Examples

1. Array + size
void print(int arr[], int n);
2. Sum array
int sum(int arr[], int n) {
    int s = 0;
    for (int i = 0; i < n; i++) s += arr[i];
    return s;
}
3. Modify in place
void scale(int arr[], int n, int k) {
    for (int i = 0; i < n; i++) arr[i] *= k;
}
4. 2D array param
void show(int m[][3], int rows);
5. Return from array
int maxVal(int arr[], int n);
6. const array param
void print(const int arr[], int n);
Array Passing Rules:
  • Arrays are always passed by reference (address)
  • For 1D arrays: Pass size as separate parameter
  • For 2D arrays: Must specify column size in parameter
  • int arr[] and int *arr are equivalent in parameters
  • Changes made to array in function affect original
  • Use const keyword for read-only array parameters

4. Function Overloading

Function overloading allows multiple functions with the same name but different parameters. The compiler distinguishes them based on parameter count or types.

Overloading Syntax
// DIFFERENT PARAMETER COUNT
int add(int a, int b);
int add(int a, int b, int c);

// DIFFERENT PARAMETER TYPES
int add(int a, int b);
double add(double a, double b);

// DIFFERENT PARAMETER ORDER
void print(int id, string name);
void print(string name, int id);

Examples

1. int + int
int add(int a, int b) {
    return a + b;
}
2. double + double
double add(double a, double b) {
    return a + b;
}
3. Three ints
int add(int a, int b, int c) {
    return a + b + c;
}
4. print int
void print(int x) { cout << x; }
5. print string
void print(string s) { cout << s; }
6. Compiler picks best
add(1, 2);      // int version
add(1.5, 2.5);  // double version
Overloading Best Practices:
  • Overload based on parameter types/count, not return type
  • Maintain consistent functionality across overloads
  • Avoid ambiguous overloads that confuse compiler
  • Use default parameters as alternative to overloading
  • Document each overload's purpose clearly

5. Recursive Functions

A recursive function calls itself to solve a problem. It must have a base case (stopping condition) and a recursive case (calls itself).

Recursion Structure
return_type function(parameters) {
    // BASE CASE (stopping condition)
    if (stopping_condition) {
        return base_value;
    }
    
    // RECURSIVE CASE (call itself)
    return function(modified_parameters);
}

Examples

1. Factorial
int fact(int n) {
    if (n <= 1) return 1;
    return n * fact(n - 1);
}
2. Fibonacci
int fib(int n) {
    if (n <= 1) return n;
    return fib(n-1) + fib(n-2);
}
3. Sum 1 to n
int sum(int n) {
    if (n == 0) return 0;
    return n + sum(n - 1);
}
4. Power
int power(int b, int e) {
    if (e == 0) return 1;
    return b * power(b, e - 1);
}
5. GCD
int gcd(int a, int b) {
    if (b == 0) return a;
    return gcd(b, a % b);
}
6. Base case required
// always define stopping condition
Recursion Warnings:
  • Always have a base case to prevent infinite recursion
  • Recursion can be inefficient (stack overhead, repeated calculations)
  • May cause stack overflow for deep recursion
  • Use iteration when possible for better performance
  • Consider memoization (caching) for optimization

6. Best Practices and Common Mistakes

Function Best Practices
  • One function = One responsibility
  • Use descriptive function names
  • Keep functions small (< 50 lines)
  • Use const references for large read-only parameters
  • Document function purpose, parameters, return value
  • Validate parameters at function entry
  • Use function prototypes for better organization
Common Mistakes
  • Forgetting return statement in non-void function
  • Mismatched function prototype and definition
  • Passing wrong array size to functions
  • Infinite recursion without base case
  • Global variables instead of parameters
  • Functions that are too long and complex
  • Not handling edge cases in parameters

Examples

1. Single purpose
int square(int x) { return x * x; }
2. Meaningful names
bool isPrime(int n);  // not check()
3. const ref for strings
void log(const string& msg);
4. Avoid global state
int add(int a, int b) {
    return a + b;  // no globals
}
5. Return by value small
int getMax(int a, int b) {
    return (a > b) ? a : b;
}
6. Document parameters
// arr: input array, n: size (n > 0)
int sum(int arr[], int n);

Frequently asked questions

When should I pass by const reference?

For large objects you will not modify—avoids copy cost while preventing accidental changes.

Can return type alone overload a function?

No—signature includes name and parameter types only, not return type.

What does inline mean?

Hint to embed code at call site; modern compilers decide; still need ODR rules for multiple translation units.