Skip to main content

Functions

Functions in C++

In C++, functions are the building blocks of reusable and modular code. Whether you're calculating the sum of numbers or managing a large application, functions help keep your code organized and maintainable.

Let’s dive into the key concepts that define how functions work in C++.

Defining a Function

A function in C++ is a self-contained block of code designed to perform a specific task. Every function must be declared and defined properly.

Here:

  • int is the return type.
  • add is the function name.
  • (int a, int b) are the parameters.
  • return a + b; sends back the result to the caller.

Function Declaration and Its Use Cases

You can declare a function before using it. This is also known as a function prototype, often used to declare functions before main() when the full function definition is written later in the code.

Use cases:

  • Useful in header files.
  • Allows logical ordering of code (e.g., keeping main() at the top).
  • Helps with large codebases by separating interface and implementation.

Calling a Function

You call a function by writing its name and passing the required arguments.

This triggers execution of the function’s logic, and the return value can be stored or used directly.

Function Arguments

Functions can take input values called arguments or parameters. These can be passed in several ways:

➤ Pass by Value (default)

The actual value is copied. Modifications inside the function won’t affect the original.

➤ Pass by Reference

Useful when you want to modify the actual argument.

➤ Pass by Pointer

Another way to manipulate original variables.

Default Parameter Values

C++ allows you to define default values for parameters, so the caller can omit them.

Usage:

Note: Default arguments must be defined from right to left in the parameter list.

Function Overloading

Function overloading allows multiple functions with the same name but different parameter lists.

Key Points:

  • Overloaded functions must differ in the number or types of parameters.
  • Return type alone cannot distinguish overloaded functions.

Function Overriding

Function overriding allows a derived class to provide a specific implementation of a function already defined in its base class.

Key Points:

  • The base class function must be marked with the virtual keyword.
  • The derived class function must have the same signature as the base class function.
  • Enables runtime polymorphism.

Inline Functions

Inline functions suggest to the compiler to insert the function's body at the point of each function call, potentially reducing overhead.

Note: The compiler may ignore the inline suggestion if the function is too complex.

Recursive Functions

A recursive function calls itself to solve a problem by breaking it down into smaller sub-problems.

Key Points:

  • Must have a base case to prevent infinite recursion.
  • Useful for problems that can be divided into similar sub-problems.

Lambda Expressions

Lambda expressions are anonymous functions that can be defined within functions and passed as arguments.

Syntax:

Key Points:

  • Introduced in C++11.
  • Useful for short, concise function definitions.
  • Can capture variables from the surrounding scope.

Understanding Function Calls

When a function is invoked in C++, the system performs several operations to manage the execution flow and memory. This involves creating a stack frame on the call stack, which stores information about the function's execution context.

What is the Call Stack?
The call stack is a stack data structure that stores information about active subroutines or functions within a program. Each time a function is called, a new stack frame is pushed onto the stack. When the function execution is complete, its stack frame is popped off the stack, and control returns to the calling function.

Structure of a Stack Frame
A typical stack frame contains:

  • Return Address: The point in the code to return to after the function completes.
  • Function Parameters: The arguments passed to the function.
  • Local Variables: Variables declared within the function.
  • Saved Registers: Registers that need to be preserved across function calls.
  • Previous Frame Pointer: A reference to the previous stack frame, facilitating stack unwinding.

Function Call Process

Here's a step-by-step breakdown of what happens during a function call:

  1. Caller Prepares Arguments: The calling function evaluates and places the arguments for the called function, typically pushing them onto the stack.
  2. Call Instruction: The call instruction is executed, which does two things:
    • Pushes the return address onto the stack.
    • Transfers control to the called function.
  3. Callee Prologue:
    • Saves the current frame pointer (e.g., EBP register) onto the stack.
    • Sets the new frame pointer to the current stack pointer (ESP).
    • Allocates space for local variables by adjusting the stack pointer.
  4. Function Execution: The function executes its code, using the stack frame to access parameters and local variables.
  5. Callee Epilogue:
    • Restores saved registers and the previous frame pointer.
    • Retrieves the return address from the stack.
  6. Return Instruction: The ret instruction is executed, popping the return address off the stack and transferring control back to the calling function.

Recursive Function Calls

In recursive functions, each call creates a new stack frame on the call stack. This allows each recursive call to maintain its own execution context. However, excessive recursion can lead to a stack overflow, as the call stack has limited size.

Stack Overflow

A stack overflow occurs when the call stack exceeds its maximum size, often due to deep or infinite recursion. This results in a program crash or undefined behaviour.

Example: Function Call Stack

Consider the following C++ code:

Execution Flow:

  1. main is called:
    • Stack frame for main is created.
  2. funcA is called from main:
    • Stack frame for funcA is pushed onto the stack.
  3. funcB is called from funcA:
    • Stack frame for funcB is pushed onto the stack.
  4. funcB completes:
    • Stack frame for funcB is popped.
  5. funcA completes:
    • Stack frame for funcA is popped.
  6. main completes:
    • Stack frame for main is popped.